go-pear.phar 3.5 MB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828248292483024831248322483324834248352483624837248382483924840248412484224843248442484524846248472484824849248502485124852248532485424855248562485724858248592486024861248622486324864248652486624867248682486924870248712487224873248742487524876248772487824879248802488124882248832488424885248862488724888248892489024891248922489324894248952489624897248982489924900249012490224903249042490524906249072490824909249102491124912249132491424915249162491724918249192492024921249222492324924249252492624927249282492924930249312493224933249342493524936249372493824939249402494124942249432494424945249462494724948249492495024951249522495324954249552495624957249582495924960249612496224963249642496524966249672496824969249702497124972249732497424975249762497724978249792498024981249822498324984249852498624987249882498924990249912499224993249942499524996249972499824999250002500125002250032500425005250062500725008250092501025011250122501325014250152501625017250182501925020250212502225023250242502525026250272502825029250302503125032250332503425035250362503725038250392504025041250422504325044250452504625047250482504925050250512505225053250542505525056250572505825059250602506125062250632506425065250662506725068250692507025071250722507325074250752507625077250782507925080250812508225083250842508525086250872508825089250902509125092250932509425095250962509725098250992510025101251022510325104251052510625107251082510925110251112511225113251142511525116251172511825119251202512125122251232512425125251262512725128251292513025131251322513325134251352513625137251382513925140251412514225143251442514525146251472514825149251502515125152251532515425155251562515725158251592516025161251622516325164251652516625167251682516925170251712517225173251742517525176251772517825179251802518125182251832518425185251862518725188251892519025191251922519325194251952519625197251982519925200252012520225203252042520525206252072520825209252102521125212252132521425215252162521725218252192522025221252222522325224252252522625227252282522925230252312523225233252342523525236252372523825239252402524125242252432524425245252462524725248252492525025251252522525325254252552525625257252582525925260252612526225263252642526525266252672526825269252702527125272252732527425275252762527725278252792528025281252822528325284252852528625287252882528925290252912529225293252942529525296252972529825299253002530125302253032530425305253062530725308253092531025311253122531325314253152531625317253182531925320253212532225323253242532525326253272532825329253302533125332253332533425335253362533725338253392534025341253422534325344253452534625347253482534925350253512535225353253542535525356253572535825359253602536125362253632536425365253662536725368253692537025371253722537325374253752537625377253782537925380253812538225383253842538525386253872538825389253902539125392253932539425395253962539725398253992540025401254022540325404254052540625407254082540925410254112541225413254142541525416254172541825419254202542125422254232542425425254262542725428254292543025431254322543325434254352543625437254382543925440254412544225443254442544525446254472544825449254502545125452254532545425455254562545725458254592546025461254622546325464254652546625467254682546925470254712547225473254742547525476254772547825479254802548125482254832548425485254862548725488254892549025491254922549325494254952549625497254982549925500255012550225503255042550525506255072550825509255102551125512255132551425515255162551725518255192552025521255222552325524255252552625527255282552925530255312553225533255342553525536255372553825539255402554125542255432554425545255462554725548255492555025551255522555325554255552555625557255582555925560255612556225563255642556525566255672556825569255702557125572255732557425575255762557725578255792558025581255822558325584255852558625587255882558925590255912559225593255942559525596255972559825599256002560125602256032560425605256062560725608256092561025611256122561325614256152561625617256182561925620256212562225623256242562525626256272562825629256302563125632256332563425635256362563725638256392564025641256422564325644256452564625647256482564925650256512565225653256542565525656256572565825659256602566125662256632566425665256662566725668256692567025671256722567325674256752567625677256782567925680256812568225683256842568525686256872568825689256902569125692256932569425695256962569725698256992570025701257022570325704257052570625707257082570925710257112571225713257142571525716257172571825719257202572125722257232572425725257262572725728257292573025731257322573325734257352573625737257382573925740257412574225743257442574525746257472574825749257502575125752257532575425755257562575725758257592576025761257622576325764257652576625767257682576925770257712577225773257742577525776257772577825779257802578125782257832578425785257862578725788257892579025791257922579325794257952579625797257982579925800258012580225803258042580525806258072580825809258102581125812258132581425815258162581725818258192582025821258222582325824258252582625827258282582925830258312583225833258342583525836258372583825839258402584125842258432584425845258462584725848258492585025851258522585325854258552585625857258582585925860258612586225863258642586525866258672586825869258702587125872258732587425875258762587725878258792588025881258822588325884258852588625887258882588925890258912589225893258942589525896258972589825899259002590125902259032590425905259062590725908259092591025911259122591325914259152591625917259182591925920259212592225923259242592525926259272592825929259302593125932259332593425935259362593725938259392594025941259422594325944259452594625947259482594925950259512595225953259542595525956259572595825959259602596125962259632596425965259662596725968259692597025971259722597325974259752597625977259782597925980259812598225983259842598525986259872598825989259902599125992259932599425995259962599725998259992600026001260022600326004260052600626007260082600926010260112601226013260142601526016260172601826019260202602126022260232602426025260262602726028260292603026031260322603326034260352603626037260382603926040260412604226043260442604526046260472604826049260502605126052260532605426055260562605726058260592606026061260622606326064260652606626067260682606926070260712607226073260742607526076260772607826079260802608126082260832608426085260862608726088260892609026091260922609326094260952609626097260982609926100261012610226103261042610526106261072610826109261102611126112261132611426115261162611726118261192612026121261222612326124261252612626127261282612926130261312613226133261342613526136261372613826139261402614126142261432614426145261462614726148261492615026151261522615326154261552615626157261582615926160261612616226163261642616526166261672616826169261702617126172261732617426175261762617726178261792618026181261822618326184261852618626187261882618926190261912619226193261942619526196261972619826199262002620126202262032620426205262062620726208262092621026211262122621326214262152621626217262182621926220262212622226223262242622526226262272622826229262302623126232262332623426235262362623726238262392624026241262422624326244262452624626247262482624926250262512625226253262542625526256262572625826259262602626126262262632626426265262662626726268262692627026271262722627326274262752627626277262782627926280262812628226283262842628526286262872628826289262902629126292262932629426295262962629726298262992630026301263022630326304263052630626307263082630926310263112631226313263142631526316263172631826319263202632126322263232632426325263262632726328263292633026331263322633326334263352633626337263382633926340263412634226343263442634526346263472634826349263502635126352263532635426355263562635726358263592636026361263622636326364263652636626367263682636926370263712637226373263742637526376263772637826379263802638126382263832638426385263862638726388263892639026391263922639326394263952639626397263982639926400264012640226403264042640526406264072640826409264102641126412264132641426415264162641726418264192642026421264222642326424264252642626427264282642926430264312643226433264342643526436264372643826439264402644126442264432644426445264462644726448264492645026451264522645326454264552645626457264582645926460264612646226463264642646526466264672646826469264702647126472264732647426475264762647726478264792648026481264822648326484264852648626487264882648926490264912649226493264942649526496264972649826499265002650126502265032650426505265062650726508265092651026511265122651326514265152651626517265182651926520265212652226523265242652526526265272652826529265302653126532265332653426535265362653726538265392654026541265422654326544265452654626547265482654926550265512655226553265542655526556265572655826559265602656126562265632656426565265662656726568265692657026571265722657326574265752657626577265782657926580265812658226583265842658526586265872658826589265902659126592265932659426595265962659726598265992660026601266022660326604266052660626607266082660926610266112661226613266142661526616266172661826619266202662126622266232662426625266262662726628266292663026631266322663326634266352663626637266382663926640266412664226643266442664526646266472664826649266502665126652266532665426655266562665726658266592666026661266622666326664266652666626667266682666926670266712667226673266742667526676266772667826679266802668126682266832668426685266862668726688266892669026691266922669326694266952669626697266982669926700267012670226703267042670526706267072670826709267102671126712267132671426715267162671726718267192672026721267222672326724267252672626727267282672926730267312673226733267342673526736267372673826739267402674126742267432674426745267462674726748267492675026751267522675326754267552675626757267582675926760267612676226763267642676526766267672676826769267702677126772267732677426775267762677726778267792678026781267822678326784267852678626787267882678926790267912679226793267942679526796267972679826799268002680126802268032680426805268062680726808268092681026811268122681326814268152681626817268182681926820268212682226823268242682526826268272682826829268302683126832268332683426835268362683726838268392684026841268422684326844268452684626847268482684926850268512685226853268542685526856268572685826859268602686126862268632686426865268662686726868268692687026871268722687326874268752687626877268782687926880268812688226883268842688526886268872688826889268902689126892268932689426895268962689726898268992690026901269022690326904269052690626907269082690926910269112691226913269142691526916269172691826919269202692126922269232692426925269262692726928269292693026931269322693326934269352693626937269382693926940269412694226943269442694526946269472694826949269502695126952269532695426955269562695726958269592696026961269622696326964269652696626967269682696926970269712697226973269742697526976269772697826979269802698126982269832698426985269862698726988269892699026991269922699326994269952699626997269982699927000270012700227003270042700527006270072700827009270102701127012270132701427015270162701727018270192702027021270222702327024270252702627027270282702927030270312703227033270342703527036270372703827039270402704127042270432704427045270462704727048270492705027051270522705327054270552705627057270582705927060270612706227063270642706527066270672706827069270702707127072270732707427075270762707727078270792708027081270822708327084270852708627087270882708927090270912709227093270942709527096270972709827099271002710127102271032710427105271062710727108271092711027111271122711327114271152711627117271182711927120271212712227123271242712527126271272712827129271302713127132271332713427135271362713727138271392714027141271422714327144271452714627147271482714927150271512715227153271542715527156271572715827159271602716127162271632716427165271662716727168271692717027171271722717327174271752717627177271782717927180271812718227183271842718527186271872718827189271902719127192271932719427195271962719727198271992720027201272022720327204272052720627207272082720927210272112721227213272142721527216272172721827219272202722127222272232722427225272262722727228272292723027231272322723327234272352723627237272382723927240272412724227243272442724527246272472724827249272502725127252272532725427255272562725727258272592726027261272622726327264272652726627267272682726927270272712727227273272742727527276272772727827279272802728127282272832728427285272862728727288272892729027291272922729327294272952729627297272982729927300273012730227303273042730527306273072730827309273102731127312273132731427315273162731727318273192732027321273222732327324273252732627327273282732927330273312733227333273342733527336273372733827339273402734127342273432734427345273462734727348273492735027351273522735327354273552735627357273582735927360273612736227363273642736527366273672736827369273702737127372273732737427375273762737727378273792738027381273822738327384273852738627387273882738927390273912739227393273942739527396273972739827399274002740127402274032740427405274062740727408274092741027411274122741327414274152741627417274182741927420274212742227423274242742527426274272742827429274302743127432274332743427435274362743727438274392744027441274422744327444274452744627447274482744927450274512745227453274542745527456274572745827459274602746127462274632746427465274662746727468274692747027471274722747327474274752747627477274782747927480274812748227483274842748527486274872748827489274902749127492274932749427495274962749727498274992750027501275022750327504275052750627507275082750927510275112751227513275142751527516275172751827519275202752127522275232752427525275262752727528275292753027531275322753327534275352753627537275382753927540275412754227543275442754527546275472754827549275502755127552275532755427555275562755727558275592756027561275622756327564275652756627567275682756927570275712757227573275742757527576275772757827579275802758127582275832758427585275862758727588275892759027591275922759327594275952759627597275982759927600276012760227603276042760527606276072760827609276102761127612276132761427615276162761727618276192762027621276222762327624276252762627627276282762927630276312763227633276342763527636276372763827639276402764127642276432764427645276462764727648276492765027651276522765327654276552765627657276582765927660276612766227663276642766527666276672766827669276702767127672276732767427675276762767727678276792768027681276822768327684276852768627687276882768927690276912769227693276942769527696276972769827699277002770127702277032770427705277062770727708277092771027711277122771327714277152771627717277182771927720277212772227723277242772527726277272772827729277302773127732277332773427735277362773727738277392774027741277422774327744277452774627747277482774927750277512775227753277542775527756277572775827759277602776127762277632776427765277662776727768277692777027771277722777327774277752777627777277782777927780277812778227783277842778527786277872778827789277902779127792277932779427795277962779727798277992780027801278022780327804278052780627807278082780927810278112781227813278142781527816278172781827819278202782127822278232782427825278262782727828278292783027831278322783327834278352783627837278382783927840278412784227843278442784527846278472784827849278502785127852278532785427855278562785727858278592786027861278622786327864278652786627867278682786927870278712787227873278742787527876278772787827879278802788127882278832788427885278862788727888278892789027891278922789327894278952789627897278982789927900279012790227903279042790527906279072790827909279102791127912279132791427915279162791727918279192792027921279222792327924279252792627927279282792927930279312793227933279342793527936279372793827939279402794127942279432794427945279462794727948279492795027951279522795327954279552795627957279582795927960279612796227963279642796527966279672796827969279702797127972279732797427975279762797727978279792798027981279822798327984279852798627987279882798927990279912799227993279942799527996279972799827999280002800128002280032800428005280062800728008280092801028011280122801328014280152801628017280182801928020280212802228023280242802528026280272802828029280302803128032280332803428035280362803728038280392804028041280422804328044280452804628047280482804928050280512805228053280542805528056280572805828059280602806128062280632806428065280662806728068280692807028071280722807328074280752807628077280782807928080280812808228083280842808528086280872808828089280902809128092280932809428095280962809728098280992810028101281022810328104281052810628107281082810928110281112811228113281142811528116281172811828119281202812128122281232812428125281262812728128281292813028131281322813328134281352813628137281382813928140281412814228143281442814528146281472814828149281502815128152281532815428155281562815728158281592816028161281622816328164281652816628167281682816928170281712817228173281742817528176281772817828179281802818128182281832818428185281862818728188281892819028191281922819328194281952819628197281982819928200282012820228203282042820528206282072820828209282102821128212282132821428215282162821728218282192822028221282222822328224282252822628227282282822928230282312823228233282342823528236282372823828239282402824128242282432824428245282462824728248282492825028251282522825328254282552825628257282582825928260282612826228263282642826528266282672826828269282702827128272282732827428275282762827728278282792828028281282822828328284282852828628287282882828928290282912829228293282942829528296282972829828299283002830128302283032830428305283062830728308283092831028311283122831328314283152831628317283182831928320283212832228323283242832528326283272832828329283302833128332283332833428335283362833728338283392834028341283422834328344283452834628347283482834928350283512835228353283542835528356283572835828359283602836128362283632836428365283662836728368283692837028371283722837328374283752837628377283782837928380283812838228383283842838528386283872838828389283902839128392283932839428395283962839728398283992840028401284022840328404284052840628407284082840928410284112841228413284142841528416284172841828419284202842128422284232842428425284262842728428284292843028431284322843328434284352843628437284382843928440284412844228443284442844528446284472844828449284502845128452284532845428455284562845728458284592846028461284622846328464284652846628467284682846928470284712847228473284742847528476284772847828479284802848128482284832848428485284862848728488284892849028491284922849328494284952849628497284982849928500285012850228503285042850528506285072850828509285102851128512285132851428515285162851728518285192852028521285222852328524285252852628527285282852928530285312853228533285342853528536285372853828539285402854128542285432854428545285462854728548285492855028551285522855328554285552855628557285582855928560285612856228563285642856528566285672856828569285702857128572285732857428575285762857728578285792858028581285822858328584285852858628587285882858928590285912859228593285942859528596285972859828599286002860128602286032860428605286062860728608286092861028611286122861328614286152861628617286182861928620286212862228623286242862528626286272862828629286302863128632286332863428635286362863728638286392864028641286422864328644286452864628647286482864928650286512865228653286542865528656286572865828659286602866128662286632866428665286662866728668286692867028671286722867328674286752867628677286782867928680286812868228683286842868528686286872868828689286902869128692286932869428695286962869728698286992870028701287022870328704287052870628707287082870928710287112871228713287142871528716287172871828719287202872128722287232872428725287262872728728287292873028731287322873328734287352873628737287382873928740287412874228743287442874528746287472874828749287502875128752287532875428755287562875728758287592876028761287622876328764287652876628767287682876928770287712877228773287742877528776287772877828779287802878128782287832878428785287862878728788287892879028791287922879328794287952879628797287982879928800288012880228803288042880528806288072880828809288102881128812288132881428815288162881728818288192882028821288222882328824288252882628827288282882928830288312883228833288342883528836288372883828839288402884128842288432884428845288462884728848288492885028851288522885328854288552885628857288582885928860288612886228863288642886528866288672886828869288702887128872288732887428875288762887728878288792888028881288822888328884288852888628887288882888928890288912889228893288942889528896288972889828899289002890128902289032890428905289062890728908289092891028911289122891328914289152891628917289182891928920289212892228923289242892528926289272892828929289302893128932289332893428935289362893728938289392894028941289422894328944289452894628947289482894928950289512895228953289542895528956289572895828959289602896128962289632896428965289662896728968289692897028971289722897328974289752897628977289782897928980289812898228983289842898528986289872898828989289902899128992289932899428995289962899728998289992900029001290022900329004290052900629007290082900929010290112901229013290142901529016290172901829019290202902129022290232902429025290262902729028290292903029031290322903329034290352903629037290382903929040290412904229043290442904529046290472904829049290502905129052290532905429055290562905729058290592906029061290622906329064290652906629067290682906929070290712907229073290742907529076290772907829079290802908129082290832908429085290862908729088290892909029091290922909329094290952909629097290982909929100291012910229103291042910529106291072910829109291102911129112291132911429115291162911729118291192912029121291222912329124291252912629127291282912929130291312913229133291342913529136291372913829139291402914129142291432914429145291462914729148291492915029151291522915329154291552915629157291582915929160291612916229163291642916529166291672916829169291702917129172291732917429175291762917729178291792918029181291822918329184291852918629187291882918929190291912919229193291942919529196291972919829199292002920129202292032920429205292062920729208292092921029211292122921329214292152921629217292182921929220292212922229223292242922529226292272922829229292302923129232292332923429235292362923729238292392924029241292422924329244292452924629247292482924929250292512925229253292542925529256292572925829259292602926129262292632926429265292662926729268292692927029271292722927329274292752927629277292782927929280292812928229283292842928529286292872928829289292902929129292292932929429295292962929729298292992930029301293022930329304293052930629307293082930929310293112931229313293142931529316293172931829319293202932129322293232932429325293262932729328293292933029331293322933329334293352933629337293382933929340293412934229343293442934529346293472934829349293502935129352293532935429355293562935729358293592936029361293622936329364293652936629367293682936929370293712937229373293742937529376293772937829379293802938129382293832938429385293862938729388293892939029391293922939329394293952939629397293982939929400294012940229403294042940529406294072940829409294102941129412294132941429415294162941729418294192942029421294222942329424294252942629427294282942929430294312943229433294342943529436294372943829439294402944129442294432944429445294462944729448294492945029451294522945329454294552945629457294582945929460294612946229463294642946529466294672946829469294702947129472294732947429475294762947729478294792948029481294822948329484294852948629487294882948929490294912949229493294942949529496294972949829499295002950129502295032950429505295062950729508295092951029511295122951329514295152951629517295182951929520295212952229523295242952529526295272952829529295302953129532295332953429535295362953729538295392954029541295422954329544295452954629547295482954929550295512955229553295542955529556295572955829559295602956129562295632956429565295662956729568295692957029571295722957329574295752957629577295782957929580295812958229583295842958529586295872958829589295902959129592295932959429595295962959729598295992960029601296022960329604296052960629607296082960929610296112961229613296142961529616296172961829619296202962129622296232962429625296262962729628296292963029631296322963329634296352963629637296382963929640296412964229643296442964529646296472964829649296502965129652296532965429655296562965729658296592966029661296622966329664296652966629667296682966929670296712967229673296742967529676296772967829679296802968129682296832968429685296862968729688296892969029691296922969329694296952969629697296982969929700297012970229703297042970529706297072970829709297102971129712297132971429715297162971729718297192972029721297222972329724297252972629727297282972929730297312973229733297342973529736297372973829739297402974129742297432974429745297462974729748297492975029751297522975329754297552975629757297582975929760297612976229763297642976529766297672976829769297702977129772297732977429775297762977729778297792978029781297822978329784297852978629787297882978929790297912979229793297942979529796297972979829799298002980129802298032980429805298062980729808298092981029811298122981329814298152981629817298182981929820298212982229823298242982529826298272982829829298302983129832298332983429835298362983729838298392984029841298422984329844298452984629847298482984929850298512985229853298542985529856298572985829859298602986129862298632986429865298662986729868298692987029871298722987329874298752987629877298782987929880298812988229883298842988529886298872988829889298902989129892298932989429895298962989729898298992990029901299022990329904299052990629907299082990929910299112991229913299142991529916299172991829919299202992129922299232992429925299262992729928299292993029931299322993329934299352993629937299382993929940299412994229943299442994529946299472994829949299502995129952299532995429955299562995729958299592996029961299622996329964299652996629967299682996929970299712997229973299742997529976299772997829979299802998129982299832998429985299862998729988299892999029991299922999329994299952999629997299982999930000300013000230003300043000530006300073000830009300103001130012300133001430015300163001730018300193002030021300223002330024300253002630027300283002930030300313003230033300343003530036300373003830039300403004130042300433004430045300463004730048300493005030051300523005330054300553005630057300583005930060300613006230063300643006530066300673006830069300703007130072300733007430075300763007730078300793008030081300823008330084300853008630087300883008930090300913009230093300943009530096300973009830099301003010130102301033010430105301063010730108301093011030111301123011330114301153011630117301183011930120301213012230123301243012530126301273012830129301303013130132301333013430135301363013730138301393014030141301423014330144301453014630147301483014930150301513015230153301543015530156301573015830159301603016130162301633016430165301663016730168301693017030171301723017330174301753017630177301783017930180301813018230183301843018530186301873018830189301903019130192301933019430195301963019730198301993020030201302023020330204302053020630207302083020930210302113021230213302143021530216302173021830219302203022130222302233022430225302263022730228302293023030231302323023330234302353023630237302383023930240302413024230243302443024530246302473024830249302503025130252302533025430255302563025730258302593026030261302623026330264302653026630267302683026930270302713027230273302743027530276302773027830279302803028130282302833028430285302863028730288302893029030291302923029330294302953029630297302983029930300303013030230303303043030530306303073030830309303103031130312303133031430315303163031730318303193032030321303223032330324303253032630327303283032930330303313033230333303343033530336303373033830339303403034130342303433034430345303463034730348303493035030351303523035330354303553035630357303583035930360303613036230363303643036530366303673036830369303703037130372303733037430375303763037730378303793038030381303823038330384303853038630387303883038930390303913039230393303943039530396303973039830399304003040130402304033040430405304063040730408304093041030411304123041330414304153041630417304183041930420304213042230423304243042530426304273042830429304303043130432304333043430435304363043730438304393044030441304423044330444304453044630447304483044930450304513045230453304543045530456304573045830459304603046130462304633046430465304663046730468304693047030471304723047330474304753047630477304783047930480304813048230483304843048530486304873048830489304903049130492304933049430495304963049730498304993050030501305023050330504305053050630507305083050930510305113051230513305143051530516305173051830519305203052130522305233052430525305263052730528305293053030531305323053330534305353053630537305383053930540305413054230543305443054530546305473054830549305503055130552305533055430555305563055730558305593056030561305623056330564305653056630567305683056930570305713057230573305743057530576305773057830579305803058130582305833058430585305863058730588305893059030591305923059330594305953059630597305983059930600306013060230603306043060530606306073060830609306103061130612306133061430615306163061730618306193062030621306223062330624306253062630627306283062930630306313063230633306343063530636306373063830639306403064130642306433064430645306463064730648306493065030651306523065330654306553065630657306583065930660306613066230663306643066530666306673066830669306703067130672306733067430675306763067730678306793068030681306823068330684306853068630687306883068930690306913069230693306943069530696306973069830699307003070130702307033070430705307063070730708307093071030711307123071330714307153071630717307183071930720307213072230723307243072530726307273072830729307303073130732307333073430735307363073730738307393074030741307423074330744307453074630747307483074930750307513075230753307543075530756307573075830759307603076130762307633076430765307663076730768307693077030771307723077330774307753077630777307783077930780307813078230783307843078530786307873078830789307903079130792307933079430795307963079730798307993080030801308023080330804308053080630807308083080930810308113081230813308143081530816308173081830819308203082130822308233082430825308263082730828308293083030831308323083330834308353083630837308383083930840308413084230843308443084530846308473084830849308503085130852308533085430855308563085730858308593086030861308623086330864308653086630867308683086930870308713087230873308743087530876308773087830879308803088130882308833088430885308863088730888308893089030891308923089330894308953089630897308983089930900309013090230903309043090530906309073090830909309103091130912309133091430915309163091730918309193092030921309223092330924309253092630927309283092930930309313093230933309343093530936309373093830939309403094130942309433094430945309463094730948309493095030951309523095330954309553095630957309583095930960309613096230963309643096530966309673096830969309703097130972309733097430975309763097730978309793098030981309823098330984309853098630987309883098930990309913099230993309943099530996309973099830999310003100131002310033100431005310063100731008310093101031011310123101331014310153101631017310183101931020310213102231023310243102531026310273102831029310303103131032310333103431035310363103731038310393104031041310423104331044310453104631047310483104931050310513105231053310543105531056310573105831059310603106131062310633106431065310663106731068310693107031071310723107331074310753107631077310783107931080310813108231083310843108531086310873108831089310903109131092310933109431095310963109731098310993110031101311023110331104311053110631107311083110931110311113111231113311143111531116311173111831119311203112131122311233112431125311263112731128311293113031131311323113331134311353113631137311383113931140311413114231143311443114531146311473114831149311503115131152311533115431155311563115731158311593116031161311623116331164311653116631167311683116931170311713117231173311743117531176311773117831179311803118131182311833118431185311863118731188311893119031191311923119331194311953119631197311983119931200312013120231203312043120531206312073120831209312103121131212312133121431215312163121731218312193122031221312223122331224312253122631227312283122931230312313123231233312343123531236312373123831239312403124131242312433124431245312463124731248312493125031251312523125331254312553125631257312583125931260312613126231263312643126531266312673126831269312703127131272312733127431275312763127731278312793128031281312823128331284312853128631287312883128931290312913129231293312943129531296312973129831299313003130131302313033130431305313063130731308313093131031311313123131331314313153131631317313183131931320313213132231323313243132531326313273132831329313303133131332313333133431335313363133731338313393134031341313423134331344313453134631347313483134931350313513135231353313543135531356313573135831359313603136131362313633136431365313663136731368313693137031371313723137331374313753137631377313783137931380313813138231383313843138531386313873138831389313903139131392313933139431395313963139731398313993140031401314023140331404314053140631407314083140931410314113141231413314143141531416314173141831419314203142131422314233142431425314263142731428314293143031431314323143331434314353143631437314383143931440314413144231443314443144531446314473144831449314503145131452314533145431455314563145731458314593146031461314623146331464314653146631467314683146931470314713147231473314743147531476314773147831479314803148131482314833148431485314863148731488314893149031491314923149331494314953149631497314983149931500315013150231503315043150531506315073150831509315103151131512315133151431515315163151731518315193152031521315223152331524315253152631527315283152931530315313153231533315343153531536315373153831539315403154131542315433154431545315463154731548315493155031551315523155331554315553155631557315583155931560315613156231563315643156531566315673156831569315703157131572315733157431575315763157731578315793158031581315823158331584315853158631587315883158931590315913159231593315943159531596315973159831599316003160131602316033160431605316063160731608316093161031611316123161331614316153161631617316183161931620316213162231623316243162531626316273162831629316303163131632316333163431635316363163731638316393164031641316423164331644316453164631647316483164931650316513165231653316543165531656316573165831659316603166131662316633166431665316663166731668316693167031671316723167331674316753167631677316783167931680316813168231683316843168531686316873168831689316903169131692316933169431695316963169731698316993170031701317023170331704317053170631707317083170931710317113171231713317143171531716317173171831719317203172131722317233172431725317263172731728317293173031731317323173331734317353173631737317383173931740317413174231743317443174531746317473174831749317503175131752317533175431755317563175731758317593176031761317623176331764317653176631767317683176931770317713177231773317743177531776317773177831779317803178131782317833178431785317863178731788317893179031791317923179331794317953179631797317983179931800318013180231803318043180531806318073180831809318103181131812318133181431815318163181731818318193182031821318223182331824318253182631827318283182931830318313183231833318343183531836318373183831839318403184131842318433184431845318463184731848318493185031851318523185331854318553185631857318583185931860318613186231863318643186531866318673186831869318703187131872318733187431875318763187731878318793188031881318823188331884318853188631887318883188931890318913189231893318943189531896318973189831899319003190131902319033190431905319063190731908319093191031911319123191331914319153191631917319183191931920319213192231923319243192531926319273192831929319303193131932319333193431935319363193731938319393194031941319423194331944319453194631947319483194931950319513195231953319543195531956319573195831959319603196131962319633196431965319663196731968319693197031971319723197331974319753197631977319783197931980319813198231983319843198531986319873198831989319903199131992319933199431995319963199731998319993200032001320023200332004320053200632007320083200932010320113201232013320143201532016320173201832019320203202132022320233202432025320263202732028320293203032031320323203332034320353203632037320383203932040320413204232043320443204532046320473204832049320503205132052320533205432055320563205732058320593206032061320623206332064320653206632067320683206932070320713207232073320743207532076320773207832079320803208132082320833208432085320863208732088320893209032091320923209332094320953209632097320983209932100321013210232103321043210532106321073210832109321103211132112321133211432115321163211732118321193212032121321223212332124321253212632127321283212932130321313213232133321343213532136321373213832139321403214132142321433214432145321463214732148321493215032151321523215332154321553215632157321583215932160321613216232163321643216532166321673216832169321703217132172321733217432175321763217732178321793218032181321823218332184321853218632187321883218932190321913219232193321943219532196321973219832199322003220132202322033220432205322063220732208322093221032211322123221332214322153221632217322183221932220322213222232223322243222532226322273222832229322303223132232322333223432235322363223732238322393224032241322423224332244322453224632247322483224932250322513225232253322543225532256322573225832259322603226132262322633226432265322663226732268322693227032271322723227332274322753227632277322783227932280322813228232283322843228532286322873228832289322903229132292322933229432295322963229732298322993230032301323023230332304323053230632307323083230932310323113231232313323143231532316323173231832319323203232132322323233232432325323263232732328323293233032331323323233332334323353233632337323383233932340323413234232343323443234532346323473234832349323503235132352323533235432355323563235732358323593236032361323623236332364323653236632367323683236932370323713237232373323743237532376323773237832379323803238132382323833238432385323863238732388323893239032391323923239332394323953239632397323983239932400324013240232403324043240532406324073240832409324103241132412324133241432415324163241732418324193242032421324223242332424324253242632427324283242932430324313243232433324343243532436324373243832439324403244132442324433244432445324463244732448324493245032451324523245332454324553245632457324583245932460324613246232463324643246532466324673246832469324703247132472324733247432475324763247732478324793248032481324823248332484324853248632487324883248932490324913249232493324943249532496324973249832499325003250132502325033250432505325063250732508325093251032511325123251332514325153251632517325183251932520325213252232523325243252532526325273252832529325303253132532325333253432535325363253732538325393254032541325423254332544325453254632547325483254932550325513255232553325543255532556325573255832559325603256132562325633256432565325663256732568325693257032571325723257332574325753257632577325783257932580325813258232583325843258532586325873258832589325903259132592325933259432595325963259732598325993260032601326023260332604326053260632607326083260932610326113261232613326143261532616326173261832619326203262132622326233262432625326263262732628326293263032631326323263332634326353263632637326383263932640326413264232643326443264532646326473264832649326503265132652326533265432655326563265732658326593266032661326623266332664326653266632667326683266932670326713267232673326743267532676326773267832679326803268132682326833268432685326863268732688326893269032691326923269332694326953269632697326983269932700327013270232703327043270532706327073270832709327103271132712327133271432715327163271732718327193272032721327223272332724327253272632727327283272932730327313273232733327343273532736327373273832739327403274132742327433274432745327463274732748327493275032751327523275332754327553275632757327583275932760327613276232763327643276532766327673276832769327703277132772327733277432775327763277732778327793278032781327823278332784327853278632787327883278932790327913279232793327943279532796327973279832799328003280132802328033280432805328063280732808328093281032811328123281332814328153281632817328183281932820328213282232823328243282532826328273282832829328303283132832328333283432835328363283732838328393284032841328423284332844328453284632847328483284932850328513285232853328543285532856328573285832859328603286132862328633286432865328663286732868328693287032871328723287332874328753287632877328783287932880328813288232883328843288532886328873288832889328903289132892328933289432895328963289732898328993290032901329023290332904329053290632907329083290932910329113291232913329143291532916329173291832919329203292132922329233292432925329263292732928329293293032931329323293332934329353293632937329383293932940329413294232943329443294532946329473294832949329503295132952329533295432955329563295732958329593296032961329623296332964329653296632967329683296932970329713297232973329743297532976329773297832979329803298132982329833298432985329863298732988329893299032991329923299332994329953299632997329983299933000330013300233003330043300533006330073300833009330103301133012330133301433015330163301733018330193302033021330223302333024330253302633027330283302933030330313303233033330343303533036330373303833039330403304133042330433304433045330463304733048330493305033051330523305333054330553305633057330583305933060330613306233063330643306533066330673306833069330703307133072330733307433075330763307733078330793308033081330823308333084330853308633087330883308933090330913309233093330943309533096330973309833099331003310133102331033310433105331063310733108331093311033111331123311333114331153311633117331183311933120331213312233123331243312533126331273312833129331303313133132331333313433135331363313733138331393314033141331423314333144331453314633147331483314933150331513315233153331543315533156331573315833159331603316133162331633316433165331663316733168331693317033171331723317333174331753317633177331783317933180331813318233183331843318533186331873318833189331903319133192331933319433195331963319733198331993320033201332023320333204332053320633207332083320933210332113321233213332143321533216332173321833219332203322133222332233322433225332263322733228332293323033231332323323333234332353323633237332383323933240332413324233243332443324533246332473324833249332503325133252332533325433255332563325733258332593326033261332623326333264332653326633267332683326933270332713327233273332743327533276332773327833279332803328133282332833328433285332863328733288332893329033291332923329333294332953329633297332983329933300333013330233303333043330533306333073330833309333103331133312333133331433315333163331733318333193332033321333223332333324333253332633327333283332933330333313333233333333343333533336333373333833339333403334133342333433334433345333463334733348333493335033351333523335333354333553335633357333583335933360333613336233363333643336533366333673336833369333703337133372333733337433375333763337733378333793338033381333823338333384333853338633387333883338933390333913339233393333943339533396333973339833399334003340133402334033340433405334063340733408334093341033411334123341333414334153341633417334183341933420334213342233423334243342533426334273342833429334303343133432334333343433435334363343733438334393344033441334423344333444334453344633447334483344933450334513345233453334543345533456334573345833459334603346133462334633346433465334663346733468334693347033471334723347333474334753347633477334783347933480334813348233483334843348533486334873348833489334903349133492334933349433495334963349733498334993350033501335023350333504335053350633507335083350933510335113351233513335143351533516335173351833519335203352133522335233352433525335263352733528335293353033531335323353333534335353353633537335383353933540335413354233543335443354533546335473354833549335503355133552335533355433555335563355733558335593356033561335623356333564335653356633567335683356933570335713357233573335743357533576335773357833579335803358133582335833358433585335863358733588335893359033591335923359333594335953359633597335983359933600336013360233603336043360533606336073360833609336103361133612336133361433615336163361733618336193362033621336223362333624336253362633627336283362933630336313363233633336343363533636336373363833639336403364133642336433364433645336463364733648336493365033651336523365333654336553365633657336583365933660336613366233663336643366533666336673366833669336703367133672336733367433675336763367733678336793368033681336823368333684336853368633687336883368933690336913369233693336943369533696336973369833699337003370133702337033370433705337063370733708337093371033711337123371333714337153371633717337183371933720337213372233723337243372533726337273372833729337303373133732337333373433735337363373733738337393374033741337423374333744337453374633747337483374933750337513375233753337543375533756337573375833759337603376133762337633376433765337663376733768337693377033771337723377333774337753377633777337783377933780337813378233783337843378533786337873378833789337903379133792337933379433795337963379733798337993380033801338023380333804338053380633807338083380933810338113381233813338143381533816338173381833819338203382133822338233382433825338263382733828338293383033831338323383333834338353383633837338383383933840338413384233843338443384533846338473384833849338503385133852338533385433855338563385733858338593386033861338623386333864338653386633867338683386933870338713387233873338743387533876338773387833879338803388133882338833388433885338863388733888338893389033891338923389333894338953389633897338983389933900339013390233903339043390533906339073390833909339103391133912339133391433915339163391733918339193392033921339223392333924339253392633927339283392933930339313393233933339343393533936339373393833939339403394133942339433394433945339463394733948339493395033951339523395333954339553395633957339583395933960339613396233963339643396533966339673396833969339703397133972339733397433975339763397733978339793398033981339823398333984339853398633987339883398933990339913399233993339943399533996339973399833999340003400134002340033400434005340063400734008340093401034011340123401334014340153401634017340183401934020340213402234023340243402534026340273402834029340303403134032340333403434035340363403734038340393404034041340423404334044340453404634047340483404934050340513405234053340543405534056340573405834059340603406134062340633406434065340663406734068340693407034071340723407334074340753407634077340783407934080340813408234083340843408534086340873408834089340903409134092340933409434095340963409734098340993410034101341023410334104341053410634107341083410934110341113411234113341143411534116341173411834119341203412134122341233412434125341263412734128341293413034131341323413334134341353413634137341383413934140341413414234143341443414534146341473414834149341503415134152341533415434155341563415734158341593416034161341623416334164341653416634167341683416934170341713417234173341743417534176341773417834179341803418134182341833418434185341863418734188341893419034191341923419334194341953419634197341983419934200342013420234203342043420534206342073420834209342103421134212342133421434215342163421734218342193422034221342223422334224342253422634227342283422934230342313423234233342343423534236342373423834239342403424134242342433424434245342463424734248342493425034251342523425334254342553425634257342583425934260342613426234263342643426534266342673426834269342703427134272342733427434275342763427734278342793428034281342823428334284342853428634287342883428934290342913429234293342943429534296342973429834299343003430134302343033430434305343063430734308343093431034311343123431334314343153431634317343183431934320343213432234323343243432534326343273432834329343303433134332343333433434335343363433734338343393434034341343423434334344343453434634347343483434934350343513435234353343543435534356343573435834359343603436134362343633436434365343663436734368343693437034371343723437334374343753437634377343783437934380343813438234383343843438534386343873438834389343903439134392343933439434395343963439734398343993440034401344023440334404344053440634407344083440934410344113441234413344143441534416344173441834419344203442134422344233442434425344263442734428344293443034431344323443334434344353443634437344383443934440344413444234443344443444534446344473444834449344503445134452344533445434455344563445734458344593446034461344623446334464344653446634467344683446934470344713447234473344743447534476344773447834479344803448134482344833448434485344863448734488344893449034491344923449334494344953449634497344983449934500345013450234503345043450534506345073450834509345103451134512345133451434515345163451734518345193452034521345223452334524345253452634527345283452934530345313453234533345343453534536345373453834539345403454134542345433454434545345463454734548345493455034551345523455334554345553455634557345583455934560345613456234563345643456534566345673456834569345703457134572345733457434575345763457734578345793458034581345823458334584345853458634587345883458934590345913459234593345943459534596345973459834599346003460134602346033460434605346063460734608346093461034611346123461334614346153461634617346183461934620346213462234623346243462534626346273462834629346303463134632346333463434635346363463734638346393464034641346423464334644346453464634647346483464934650346513465234653346543465534656346573465834659346603466134662346633466434665346663466734668346693467034671346723467334674346753467634677346783467934680346813468234683346843468534686346873468834689346903469134692346933469434695346963469734698346993470034701347023470334704347053470634707347083470934710347113471234713347143471534716347173471834719347203472134722347233472434725347263472734728347293473034731347323473334734347353473634737347383473934740347413474234743347443474534746347473474834749347503475134752347533475434755347563475734758347593476034761347623476334764347653476634767347683476934770347713477234773347743477534776347773477834779347803478134782347833478434785347863478734788347893479034791347923479334794347953479634797347983479934800348013480234803348043480534806348073480834809348103481134812348133481434815348163481734818348193482034821348223482334824348253482634827348283482934830348313483234833348343483534836348373483834839348403484134842348433484434845348463484734848348493485034851348523485334854348553485634857348583485934860348613486234863348643486534866348673486834869348703487134872348733487434875348763487734878348793488034881348823488334884348853488634887348883488934890348913489234893348943489534896348973489834899349003490134902349033490434905349063490734908349093491034911349123491334914349153491634917349183491934920349213492234923349243492534926349273492834929349303493134932349333493434935349363493734938349393494034941349423494334944349453494634947349483494934950349513495234953349543495534956349573495834959349603496134962349633496434965349663496734968349693497034971349723497334974349753497634977349783497934980349813498234983349843498534986349873498834989349903499134992349933499434995349963499734998349993500035001350023500335004350053500635007350083500935010350113501235013350143501535016350173501835019350203502135022350233502435025350263502735028350293503035031350323503335034350353503635037350383503935040350413504235043350443504535046350473504835049350503505135052350533505435055350563505735058350593506035061350623506335064350653506635067350683506935070350713507235073350743507535076350773507835079350803508135082350833508435085350863508735088350893509035091350923509335094350953509635097350983509935100351013510235103351043510535106351073510835109351103511135112351133511435115351163511735118351193512035121351223512335124351253512635127351283512935130351313513235133351343513535136351373513835139351403514135142351433514435145351463514735148351493515035151351523515335154351553515635157351583515935160351613516235163351643516535166351673516835169351703517135172351733517435175351763517735178351793518035181351823518335184351853518635187351883518935190351913519235193351943519535196351973519835199352003520135202352033520435205352063520735208352093521035211352123521335214352153521635217352183521935220352213522235223352243522535226352273522835229352303523135232352333523435235352363523735238352393524035241352423524335244352453524635247352483524935250352513525235253352543525535256352573525835259352603526135262352633526435265352663526735268352693527035271352723527335274352753527635277352783527935280352813528235283352843528535286352873528835289352903529135292352933529435295352963529735298352993530035301353023530335304353053530635307353083530935310353113531235313353143531535316353173531835319353203532135322353233532435325353263532735328353293533035331353323533335334353353533635337353383533935340353413534235343353443534535346353473534835349353503535135352353533535435355353563535735358353593536035361353623536335364353653536635367353683536935370353713537235373353743537535376353773537835379353803538135382353833538435385353863538735388353893539035391353923539335394353953539635397353983539935400354013540235403354043540535406354073540835409354103541135412354133541435415354163541735418354193542035421354223542335424354253542635427354283542935430354313543235433354343543535436354373543835439354403544135442354433544435445354463544735448354493545035451354523545335454354553545635457354583545935460354613546235463354643546535466354673546835469354703547135472354733547435475354763547735478354793548035481354823548335484354853548635487354883548935490354913549235493354943549535496354973549835499355003550135502355033550435505355063550735508355093551035511355123551335514355153551635517355183551935520355213552235523355243552535526355273552835529355303553135532355333553435535355363553735538355393554035541355423554335544355453554635547355483554935550355513555235553355543555535556355573555835559355603556135562355633556435565355663556735568355693557035571355723557335574355753557635577355783557935580355813558235583355843558535586355873558835589355903559135592355933559435595355963559735598355993560035601356023560335604356053560635607356083560935610356113561235613356143561535616356173561835619356203562135622356233562435625356263562735628356293563035631356323563335634356353563635637356383563935640356413564235643356443564535646356473564835649356503565135652356533565435655356563565735658356593566035661356623566335664356653566635667356683566935670356713567235673356743567535676356773567835679356803568135682356833568435685356863568735688356893569035691356923569335694356953569635697356983569935700357013570235703357043570535706357073570835709357103571135712357133571435715357163571735718357193572035721357223572335724357253572635727357283572935730357313573235733357343573535736357373573835739357403574135742357433574435745357463574735748357493575035751357523575335754357553575635757357583575935760357613576235763357643576535766357673576835769357703577135772357733577435775357763577735778357793578035781357823578335784357853578635787357883578935790357913579235793357943579535796357973579835799358003580135802358033580435805358063580735808358093581035811358123581335814358153581635817358183581935820358213582235823358243582535826358273582835829358303583135832358333583435835358363583735838358393584035841358423584335844358453584635847358483584935850358513585235853358543585535856358573585835859358603586135862358633586435865358663586735868358693587035871358723587335874358753587635877358783587935880358813588235883358843588535886358873588835889358903589135892358933589435895358963589735898358993590035901359023590335904359053590635907359083590935910359113591235913359143591535916359173591835919359203592135922359233592435925359263592735928359293593035931359323593335934359353593635937359383593935940359413594235943359443594535946359473594835949359503595135952359533595435955359563595735958359593596035961359623596335964359653596635967359683596935970359713597235973359743597535976359773597835979359803598135982359833598435985359863598735988359893599035991359923599335994359953599635997359983599936000360013600236003360043600536006360073600836009360103601136012360133601436015360163601736018360193602036021360223602336024360253602636027360283602936030360313603236033360343603536036360373603836039360403604136042360433604436045360463604736048360493605036051360523605336054360553605636057360583605936060360613606236063360643606536066360673606836069360703607136072360733607436075360763607736078360793608036081360823608336084360853608636087360883608936090360913609236093360943609536096360973609836099361003610136102361033610436105361063610736108361093611036111361123611336114361153611636117361183611936120361213612236123361243612536126361273612836129361303613136132361333613436135361363613736138361393614036141361423614336144361453614636147361483614936150361513615236153361543615536156361573615836159361603616136162361633616436165361663616736168361693617036171361723617336174361753617636177361783617936180361813618236183361843618536186361873618836189361903619136192361933619436195361963619736198361993620036201362023620336204362053620636207362083620936210362113621236213362143621536216362173621836219362203622136222362233622436225362263622736228362293623036231362323623336234362353623636237362383623936240362413624236243362443624536246362473624836249362503625136252362533625436255362563625736258362593626036261362623626336264362653626636267362683626936270362713627236273362743627536276362773627836279362803628136282362833628436285362863628736288362893629036291362923629336294362953629636297362983629936300363013630236303363043630536306363073630836309363103631136312363133631436315363163631736318363193632036321363223632336324363253632636327363283632936330363313633236333363343633536336363373633836339363403634136342363433634436345363463634736348363493635036351363523635336354363553635636357363583635936360363613636236363363643636536366363673636836369363703637136372363733637436375363763637736378363793638036381363823638336384363853638636387363883638936390363913639236393363943639536396363973639836399364003640136402364033640436405364063640736408364093641036411364123641336414364153641636417364183641936420364213642236423364243642536426364273642836429364303643136432364333643436435364363643736438364393644036441364423644336444364453644636447364483644936450364513645236453364543645536456364573645836459364603646136462364633646436465364663646736468364693647036471364723647336474364753647636477364783647936480364813648236483364843648536486364873648836489364903649136492364933649436495364963649736498364993650036501365023650336504365053650636507365083650936510365113651236513365143651536516365173651836519365203652136522365233652436525365263652736528365293653036531365323653336534365353653636537365383653936540365413654236543365443654536546365473654836549365503655136552365533655436555365563655736558365593656036561365623656336564365653656636567365683656936570365713657236573365743657536576365773657836579365803658136582365833658436585365863658736588365893659036591365923659336594365953659636597365983659936600366013660236603366043660536606366073660836609366103661136612366133661436615366163661736618366193662036621366223662336624366253662636627366283662936630366313663236633366343663536636366373663836639366403664136642366433664436645366463664736648366493665036651366523665336654366553665636657366583665936660366613666236663366643666536666366673666836669366703667136672366733667436675366763667736678366793668036681366823668336684366853668636687366883668936690366913669236693366943669536696366973669836699367003670136702367033670436705367063670736708367093671036711367123671336714367153671636717367183671936720367213672236723367243672536726367273672836729367303673136732367333673436735367363673736738367393674036741367423674336744367453674636747367483674936750367513675236753367543675536756367573675836759367603676136762367633676436765367663676736768367693677036771367723677336774367753677636777367783677936780367813678236783367843678536786367873678836789367903679136792367933679436795367963679736798367993680036801368023680336804368053680636807368083680936810368113681236813368143681536816368173681836819368203682136822368233682436825368263682736828368293683036831368323683336834368353683636837368383683936840368413684236843368443684536846368473684836849368503685136852368533685436855368563685736858368593686036861368623686336864368653686636867368683686936870368713687236873368743687536876368773687836879368803688136882368833688436885368863688736888368893689036891368923689336894368953689636897368983689936900369013690236903369043690536906369073690836909369103691136912369133691436915369163691736918369193692036921369223692336924369253692636927369283692936930369313693236933369343693536936369373693836939369403694136942369433694436945369463694736948369493695036951369523695336954369553695636957369583695936960369613696236963369643696536966369673696836969369703697136972369733697436975369763697736978369793698036981369823698336984369853698636987369883698936990369913699236993369943699536996369973699836999370003700137002370033700437005370063700737008370093701037011370123701337014370153701637017370183701937020370213702237023370243702537026370273702837029370303703137032370333703437035370363703737038370393704037041370423704337044370453704637047370483704937050370513705237053370543705537056370573705837059370603706137062370633706437065370663706737068370693707037071370723707337074370753707637077370783707937080370813708237083370843708537086370873708837089370903709137092370933709437095370963709737098370993710037101371023710337104371053710637107371083710937110371113711237113371143711537116371173711837119371203712137122371233712437125371263712737128371293713037131371323713337134371353713637137371383713937140371413714237143371443714537146371473714837149371503715137152371533715437155371563715737158371593716037161371623716337164371653716637167371683716937170371713717237173371743717537176371773717837179371803718137182371833718437185371863718737188371893719037191371923719337194371953719637197371983719937200372013720237203372043720537206372073720837209372103721137212372133721437215372163721737218372193722037221372223722337224372253722637227372283722937230372313723237233372343723537236372373723837239372403724137242372433724437245372463724737248372493725037251372523725337254372553725637257372583725937260372613726237263372643726537266372673726837269372703727137272372733727437275372763727737278372793728037281372823728337284372853728637287372883728937290372913729237293372943729537296372973729837299373003730137302373033730437305373063730737308373093731037311373123731337314373153731637317373183731937320373213732237323373243732537326373273732837329373303733137332373333733437335373363733737338373393734037341373423734337344373453734637347373483734937350373513735237353373543735537356373573735837359373603736137362373633736437365373663736737368373693737037371373723737337374373753737637377373783737937380373813738237383373843738537386373873738837389373903739137392373933739437395373963739737398373993740037401374023740337404374053740637407374083740937410374113741237413374143741537416374173741837419374203742137422374233742437425374263742737428374293743037431374323743337434374353743637437374383743937440374413744237443374443744537446374473744837449374503745137452374533745437455374563745737458374593746037461374623746337464374653746637467374683746937470374713747237473374743747537476374773747837479374803748137482374833748437485374863748737488374893749037491374923749337494374953749637497374983749937500375013750237503375043750537506375073750837509375103751137512375133751437515375163751737518375193752037521375223752337524375253752637527375283752937530375313753237533375343753537536375373753837539375403754137542375433754437545375463754737548375493755037551375523755337554375553755637557375583755937560375613756237563375643756537566375673756837569375703757137572375733757437575375763757737578375793758037581375823758337584375853758637587375883758937590375913759237593375943759537596375973759837599376003760137602376033760437605376063760737608376093761037611376123761337614376153761637617376183761937620376213762237623376243762537626376273762837629376303763137632376333763437635376363763737638376393764037641376423764337644376453764637647376483764937650376513765237653376543765537656376573765837659376603766137662376633766437665376663766737668376693767037671376723767337674376753767637677376783767937680376813768237683376843768537686376873768837689376903769137692376933769437695376963769737698376993770037701377023770337704377053770637707377083770937710377113771237713377143771537716377173771837719377203772137722377233772437725377263772737728377293773037731377323773337734377353773637737377383773937740377413774237743377443774537746377473774837749377503775137752377533775437755377563775737758377593776037761377623776337764377653776637767377683776937770377713777237773377743777537776377773777837779377803778137782377833778437785377863778737788377893779037791377923779337794377953779637797377983779937800378013780237803378043780537806378073780837809378103781137812378133781437815378163781737818378193782037821378223782337824378253782637827378283782937830378313783237833378343783537836378373783837839378403784137842378433784437845378463784737848378493785037851378523785337854378553785637857378583785937860378613786237863378643786537866378673786837869378703787137872378733787437875378763787737878378793788037881378823788337884378853788637887378883788937890378913789237893378943789537896378973789837899379003790137902379033790437905379063790737908379093791037911379123791337914379153791637917379183791937920379213792237923379243792537926379273792837929379303793137932379333793437935379363793737938379393794037941379423794337944379453794637947379483794937950379513795237953379543795537956379573795837959379603796137962379633796437965379663796737968379693797037971379723797337974379753797637977379783797937980379813798237983379843798537986379873798837989379903799137992379933799437995379963799737998379993800038001380023800338004380053800638007380083800938010380113801238013380143801538016380173801838019380203802138022380233802438025380263802738028380293803038031380323803338034380353803638037380383803938040380413804238043380443804538046380473804838049380503805138052380533805438055380563805738058380593806038061380623806338064380653806638067380683806938070380713807238073380743807538076380773807838079380803808138082380833808438085380863808738088380893809038091380923809338094380953809638097380983809938100381013810238103381043810538106381073810838109381103811138112381133811438115381163811738118381193812038121381223812338124381253812638127381283812938130381313813238133381343813538136381373813838139381403814138142381433814438145381463814738148381493815038151381523815338154381553815638157381583815938160381613816238163381643816538166381673816838169381703817138172381733817438175381763817738178381793818038181381823818338184381853818638187381883818938190381913819238193381943819538196381973819838199382003820138202382033820438205382063820738208382093821038211382123821338214382153821638217382183821938220382213822238223382243822538226382273822838229382303823138232382333823438235382363823738238382393824038241382423824338244382453824638247382483824938250382513825238253382543825538256382573825838259382603826138262382633826438265382663826738268382693827038271382723827338274382753827638277382783827938280382813828238283382843828538286382873828838289382903829138292382933829438295382963829738298382993830038301383023830338304383053830638307383083830938310383113831238313383143831538316383173831838319383203832138322383233832438325383263832738328383293833038331383323833338334383353833638337383383833938340383413834238343383443834538346383473834838349383503835138352383533835438355383563835738358383593836038361383623836338364383653836638367383683836938370383713837238373383743837538376383773837838379383803838138382383833838438385383863838738388383893839038391383923839338394383953839638397383983839938400384013840238403384043840538406384073840838409384103841138412384133841438415384163841738418384193842038421384223842338424384253842638427384283842938430384313843238433384343843538436384373843838439384403844138442384433844438445384463844738448384493845038451384523845338454384553845638457384583845938460384613846238463384643846538466384673846838469384703847138472384733847438475384763847738478384793848038481384823848338484384853848638487384883848938490384913849238493384943849538496384973849838499385003850138502385033850438505385063850738508385093851038511385123851338514385153851638517385183851938520385213852238523385243852538526385273852838529385303853138532385333853438535385363853738538385393854038541385423854338544385453854638547385483854938550385513855238553385543855538556385573855838559385603856138562385633856438565385663856738568385693857038571385723857338574385753857638577385783857938580385813858238583385843858538586385873858838589385903859138592385933859438595385963859738598385993860038601386023860338604386053860638607386083860938610386113861238613386143861538616386173861838619386203862138622386233862438625386263862738628386293863038631386323863338634386353863638637386383863938640386413864238643386443864538646386473864838649386503865138652386533865438655386563865738658386593866038661386623866338664386653866638667386683866938670386713867238673386743867538676386773867838679386803868138682386833868438685386863868738688386893869038691386923869338694386953869638697386983869938700387013870238703387043870538706387073870838709387103871138712387133871438715387163871738718387193872038721387223872338724387253872638727387283872938730387313873238733387343873538736387373873838739387403874138742387433874438745387463874738748387493875038751387523875338754387553875638757387583875938760387613876238763387643876538766387673876838769387703877138772387733877438775387763877738778387793878038781387823878338784387853878638787387883878938790387913879238793387943879538796387973879838799388003880138802388033880438805388063880738808388093881038811388123881338814388153881638817388183881938820388213882238823388243882538826388273882838829388303883138832388333883438835388363883738838388393884038841388423884338844388453884638847388483884938850388513885238853388543885538856388573885838859388603886138862388633886438865388663886738868388693887038871388723887338874388753887638877388783887938880388813888238883388843888538886388873888838889388903889138892388933889438895388963889738898388993890038901389023890338904389053890638907389083890938910389113891238913389143891538916389173891838919389203892138922389233892438925389263892738928389293893038931389323893338934389353893638937389383893938940389413894238943389443894538946389473894838949389503895138952389533895438955389563895738958389593896038961389623896338964389653896638967389683896938970389713897238973389743897538976389773897838979389803898138982389833898438985389863898738988389893899038991389923899338994389953899638997389983899939000390013900239003390043900539006390073900839009390103901139012390133901439015390163901739018390193902039021390223902339024390253902639027390283902939030390313903239033390343903539036390373903839039390403904139042390433904439045390463904739048390493905039051390523905339054390553905639057390583905939060390613906239063390643906539066390673906839069390703907139072390733907439075390763907739078390793908039081390823908339084390853908639087390883908939090390913909239093390943909539096390973909839099391003910139102391033910439105391063910739108391093911039111391123911339114391153911639117391183911939120391213912239123391243912539126391273912839129391303913139132391333913439135391363913739138391393914039141391423914339144391453914639147391483914939150391513915239153391543915539156391573915839159391603916139162391633916439165391663916739168391693917039171391723917339174391753917639177391783917939180391813918239183391843918539186391873918839189391903919139192391933919439195391963919739198391993920039201392023920339204392053920639207392083920939210392113921239213392143921539216392173921839219392203922139222392233922439225392263922739228392293923039231392323923339234392353923639237392383923939240392413924239243392443924539246392473924839249392503925139252392533925439255392563925739258392593926039261392623926339264392653926639267392683926939270392713927239273392743927539276392773927839279392803928139282392833928439285392863928739288392893929039291392923929339294392953929639297392983929939300393013930239303393043930539306393073930839309393103931139312393133931439315393163931739318393193932039321393223932339324393253932639327393283932939330393313933239333393343933539336393373933839339393403934139342393433934439345393463934739348393493935039351393523935339354393553935639357393583935939360393613936239363393643936539366393673936839369393703937139372393733937439375393763937739378393793938039381393823938339384393853938639387393883938939390393913939239393393943939539396393973939839399394003940139402394033940439405394063940739408394093941039411394123941339414394153941639417394183941939420394213942239423394243942539426394273942839429394303943139432394333943439435394363943739438394393944039441394423944339444394453944639447394483944939450394513945239453394543945539456394573945839459394603946139462394633946439465394663946739468394693947039471394723947339474394753947639477394783947939480394813948239483394843948539486394873948839489394903949139492394933949439495394963949739498394993950039501395023950339504395053950639507395083950939510395113951239513395143951539516395173951839519395203952139522395233952439525395263952739528395293953039531395323953339534395353953639537395383953939540395413954239543395443954539546395473954839549395503955139552395533955439555395563955739558395593956039561395623956339564395653956639567395683956939570395713957239573395743957539576395773957839579395803958139582395833958439585395863958739588395893959039591395923959339594395953959639597395983959939600396013960239603396043960539606396073960839609396103961139612396133961439615396163961739618396193962039621396223962339624396253962639627396283962939630396313963239633396343963539636396373963839639396403964139642396433964439645396463964739648396493965039651396523965339654396553965639657396583965939660396613966239663396643966539666396673966839669396703967139672396733967439675396763967739678396793968039681396823968339684396853968639687396883968939690396913969239693396943969539696396973969839699397003970139702397033970439705397063970739708397093971039711397123971339714397153971639717397183971939720397213972239723397243972539726397273972839729397303973139732397333973439735397363973739738397393974039741397423974339744397453974639747397483974939750397513975239753397543975539756397573975839759397603976139762397633976439765397663976739768397693977039771397723977339774397753977639777397783977939780397813978239783397843978539786397873978839789397903979139792397933979439795397963979739798397993980039801398023980339804398053980639807398083980939810398113981239813398143981539816398173981839819398203982139822398233982439825398263982739828398293983039831398323983339834398353983639837398383983939840398413984239843398443984539846398473984839849398503985139852398533985439855398563985739858398593986039861398623986339864398653986639867398683986939870398713987239873398743987539876398773987839879398803988139882398833988439885398863988739888398893989039891398923989339894398953989639897398983989939900399013990239903399043990539906399073990839909399103991139912399133991439915399163991739918399193992039921399223992339924399253992639927399283992939930399313993239933399343993539936399373993839939399403994139942399433994439945399463994739948399493995039951399523995339954399553995639957399583995939960399613996239963399643996539966399673996839969399703997139972399733997439975399763997739978399793998039981399823998339984399853998639987399883998939990399913999239993399943999539996399973999839999400004000140002400034000440005400064000740008400094001040011400124001340014400154001640017400184001940020400214002240023400244002540026400274002840029400304003140032400334003440035400364003740038400394004040041400424004340044400454004640047400484004940050400514005240053400544005540056400574005840059400604006140062400634006440065400664006740068400694007040071400724007340074400754007640077400784007940080400814008240083400844008540086400874008840089400904009140092400934009440095400964009740098400994010040101401024010340104401054010640107401084010940110401114011240113401144011540116401174011840119401204012140122401234012440125401264012740128401294013040131401324013340134401354013640137401384013940140401414014240143401444014540146401474014840149401504015140152401534015440155401564015740158401594016040161401624016340164401654016640167401684016940170401714017240173401744017540176401774017840179401804018140182401834018440185401864018740188401894019040191401924019340194401954019640197401984019940200402014020240203402044020540206402074020840209402104021140212402134021440215402164021740218402194022040221402224022340224402254022640227402284022940230402314023240233402344023540236402374023840239402404024140242402434024440245402464024740248402494025040251402524025340254402554025640257402584025940260402614026240263402644026540266402674026840269402704027140272402734027440275402764027740278402794028040281402824028340284402854028640287402884028940290402914029240293402944029540296402974029840299403004030140302403034030440305403064030740308403094031040311403124031340314403154031640317403184031940320403214032240323403244032540326403274032840329403304033140332403334033440335403364033740338403394034040341403424034340344403454034640347403484034940350403514035240353403544035540356403574035840359403604036140362403634036440365403664036740368403694037040371403724037340374403754037640377403784037940380403814038240383403844038540386403874038840389403904039140392403934039440395403964039740398403994040040401404024040340404404054040640407404084040940410404114041240413404144041540416404174041840419404204042140422404234042440425404264042740428404294043040431404324043340434404354043640437404384043940440404414044240443404444044540446404474044840449404504045140452404534045440455404564045740458404594046040461404624046340464404654046640467404684046940470404714047240473404744047540476404774047840479404804048140482404834048440485404864048740488404894049040491404924049340494404954049640497404984049940500405014050240503405044050540506405074050840509405104051140512405134051440515405164051740518405194052040521405224052340524405254052640527405284052940530405314053240533405344053540536405374053840539405404054140542405434054440545405464054740548405494055040551405524055340554405554055640557405584055940560405614056240563405644056540566405674056840569405704057140572405734057440575405764057740578405794058040581405824058340584405854058640587405884058940590405914059240593405944059540596405974059840599406004060140602406034060440605406064060740608406094061040611406124061340614406154061640617406184061940620406214062240623406244062540626406274062840629406304063140632406334063440635406364063740638406394064040641406424064340644406454064640647406484064940650406514065240653406544065540656406574065840659406604066140662406634066440665406664066740668406694067040671406724067340674406754067640677406784067940680406814068240683406844068540686406874068840689406904069140692406934069440695406964069740698406994070040701407024070340704407054070640707407084070940710407114071240713407144071540716407174071840719407204072140722407234072440725407264072740728407294073040731407324073340734407354073640737407384073940740407414074240743407444074540746407474074840749407504075140752407534075440755407564075740758407594076040761407624076340764407654076640767407684076940770407714077240773407744077540776407774077840779407804078140782407834078440785407864078740788407894079040791407924079340794407954079640797407984079940800408014080240803408044080540806408074080840809408104081140812408134081440815408164081740818408194082040821408224082340824408254082640827408284082940830408314083240833408344083540836408374083840839408404084140842408434084440845408464084740848408494085040851408524085340854408554085640857408584085940860408614086240863408644086540866408674086840869408704087140872408734087440875408764087740878408794088040881408824088340884408854088640887408884088940890408914089240893408944089540896408974089840899409004090140902409034090440905409064090740908409094091040911409124091340914409154091640917409184091940920409214092240923409244092540926409274092840929409304093140932409334093440935409364093740938409394094040941409424094340944409454094640947409484094940950409514095240953409544095540956409574095840959409604096140962409634096440965409664096740968409694097040971409724097340974409754097640977409784097940980409814098240983409844098540986409874098840989409904099140992409934099440995409964099740998409994100041001410024100341004410054100641007410084100941010410114101241013410144101541016410174101841019410204102141022410234102441025410264102741028410294103041031410324103341034410354103641037410384103941040410414104241043410444104541046410474104841049410504105141052410534105441055410564105741058410594106041061410624106341064410654106641067410684106941070410714107241073410744107541076410774107841079410804108141082410834108441085410864108741088410894109041091410924109341094410954109641097410984109941100411014110241103411044110541106411074110841109411104111141112411134111441115411164111741118411194112041121411224112341124411254112641127411284112941130411314113241133411344113541136411374113841139411404114141142411434114441145411464114741148411494115041151411524115341154411554115641157411584115941160411614116241163411644116541166411674116841169411704117141172411734117441175411764117741178411794118041181411824118341184411854118641187411884118941190411914119241193411944119541196411974119841199412004120141202412034120441205412064120741208412094121041211412124121341214412154121641217412184121941220412214122241223412244122541226412274122841229412304123141232412334123441235412364123741238412394124041241412424124341244412454124641247412484124941250412514125241253412544125541256412574125841259412604126141262412634126441265412664126741268412694127041271412724127341274412754127641277412784127941280412814128241283412844128541286412874128841289412904129141292412934129441295412964129741298412994130041301413024130341304413054130641307413084130941310413114131241313413144131541316413174131841319413204132141322413234132441325413264132741328413294133041331413324133341334413354133641337413384133941340413414134241343413444134541346413474134841349413504135141352413534135441355413564135741358413594136041361413624136341364413654136641367413684136941370413714137241373413744137541376413774137841379413804138141382413834138441385413864138741388413894139041391413924139341394413954139641397413984139941400414014140241403414044140541406414074140841409414104141141412414134141441415414164141741418414194142041421414224142341424414254142641427414284142941430414314143241433414344143541436414374143841439414404144141442414434144441445414464144741448414494145041451414524145341454414554145641457414584145941460414614146241463414644146541466414674146841469414704147141472414734147441475414764147741478414794148041481414824148341484414854148641487414884148941490414914149241493414944149541496414974149841499415004150141502415034150441505415064150741508415094151041511415124151341514415154151641517415184151941520415214152241523415244152541526415274152841529415304153141532415334153441535415364153741538415394154041541415424154341544415454154641547415484154941550415514155241553415544155541556415574155841559415604156141562415634156441565415664156741568415694157041571415724157341574415754157641577415784157941580415814158241583415844158541586415874158841589415904159141592415934159441595415964159741598415994160041601416024160341604416054160641607416084160941610416114161241613416144161541616416174161841619416204162141622416234162441625416264162741628416294163041631416324163341634416354163641637416384163941640416414164241643416444164541646416474164841649416504165141652416534165441655416564165741658416594166041661416624166341664416654166641667416684166941670416714167241673416744167541676416774167841679416804168141682416834168441685416864168741688416894169041691416924169341694416954169641697416984169941700417014170241703417044170541706417074170841709417104171141712417134171441715417164171741718417194172041721417224172341724417254172641727417284172941730417314173241733417344173541736417374173841739417404174141742417434174441745417464174741748417494175041751417524175341754417554175641757417584175941760417614176241763417644176541766417674176841769417704177141772417734177441775417764177741778417794178041781417824178341784417854178641787417884178941790417914179241793417944179541796417974179841799418004180141802418034180441805418064180741808418094181041811418124181341814418154181641817418184181941820418214182241823418244182541826418274182841829418304183141832418334183441835418364183741838418394184041841418424184341844418454184641847418484184941850418514185241853418544185541856418574185841859418604186141862418634186441865418664186741868418694187041871418724187341874418754187641877418784187941880418814188241883418844188541886418874188841889418904189141892418934189441895418964189741898418994190041901419024190341904419054190641907419084190941910419114191241913419144191541916419174191841919419204192141922419234192441925419264192741928419294193041931419324193341934419354193641937419384193941940419414194241943419444194541946419474194841949419504195141952419534195441955419564195741958419594196041961419624196341964419654196641967419684196941970419714197241973419744197541976419774197841979419804198141982419834198441985419864198741988419894199041991419924199341994419954199641997419984199942000420014200242003420044200542006420074200842009420104201142012420134201442015420164201742018420194202042021420224202342024420254202642027420284202942030420314203242033420344203542036420374203842039420404204142042420434204442045420464204742048420494205042051420524205342054420554205642057420584205942060420614206242063420644206542066420674206842069420704207142072420734207442075420764207742078420794208042081420824208342084420854208642087420884208942090420914209242093420944209542096420974209842099421004210142102421034210442105421064210742108421094211042111421124211342114421154211642117421184211942120421214212242123421244212542126421274212842129421304213142132421334213442135421364213742138421394214042141421424214342144421454214642147421484214942150421514215242153421544215542156421574215842159421604216142162421634216442165421664216742168421694217042171421724217342174421754217642177421784217942180421814218242183421844218542186421874218842189421904219142192421934219442195421964219742198421994220042201422024220342204422054220642207422084220942210422114221242213422144221542216422174221842219422204222142222422234222442225422264222742228422294223042231422324223342234422354223642237422384223942240422414224242243422444224542246422474224842249422504225142252422534225442255422564225742258422594226042261422624226342264422654226642267422684226942270422714227242273422744227542276422774227842279422804228142282422834228442285422864228742288422894229042291422924229342294422954229642297422984229942300423014230242303423044230542306423074230842309423104231142312423134231442315423164231742318423194232042321423224232342324423254232642327423284232942330423314233242333423344233542336423374233842339423404234142342423434234442345423464234742348423494235042351423524235342354423554235642357423584235942360423614236242363423644236542366423674236842369423704237142372423734237442375423764237742378423794238042381423824238342384423854238642387423884238942390423914239242393423944239542396423974239842399424004240142402424034240442405424064240742408424094241042411424124241342414424154241642417424184241942420424214242242423424244242542426424274242842429424304243142432424334243442435424364243742438424394244042441424424244342444424454244642447424484244942450424514245242453424544245542456424574245842459424604246142462424634246442465424664246742468424694247042471424724247342474424754247642477424784247942480424814248242483424844248542486424874248842489424904249142492424934249442495424964249742498424994250042501425024250342504425054250642507425084250942510425114251242513425144251542516425174251842519425204252142522425234252442525425264252742528425294253042531425324253342534425354253642537425384253942540425414254242543425444254542546425474254842549425504255142552425534255442555425564255742558425594256042561425624256342564425654256642567425684256942570425714257242573425744257542576425774257842579425804258142582425834258442585425864258742588425894259042591425924259342594425954259642597425984259942600426014260242603426044260542606426074260842609426104261142612426134261442615426164261742618426194262042621426224262342624426254262642627426284262942630426314263242633426344263542636426374263842639426404264142642426434264442645426464264742648426494265042651426524265342654426554265642657426584265942660426614266242663426644266542666426674266842669426704267142672426734267442675426764267742678426794268042681426824268342684426854268642687426884268942690426914269242693426944269542696426974269842699427004270142702427034270442705427064270742708427094271042711427124271342714427154271642717427184271942720427214272242723427244272542726427274272842729427304273142732427334273442735427364273742738427394274042741427424274342744427454274642747427484274942750427514275242753427544275542756427574275842759427604276142762427634276442765427664276742768427694277042771427724277342774427754277642777427784277942780427814278242783427844278542786427874278842789427904279142792427934279442795427964279742798427994280042801428024280342804428054280642807428084280942810428114281242813428144281542816428174281842819428204282142822428234282442825428264282742828428294283042831428324283342834428354283642837428384283942840428414284242843428444284542846428474284842849428504285142852428534285442855428564285742858428594286042861428624286342864428654286642867428684286942870428714287242873428744287542876428774287842879428804288142882428834288442885428864288742888428894289042891428924289342894428954289642897428984289942900429014290242903429044290542906429074290842909429104291142912429134291442915429164291742918429194292042921429224292342924429254292642927429284292942930429314293242933429344293542936429374293842939429404294142942429434294442945429464294742948429494295042951429524295342954429554295642957429584295942960429614296242963429644296542966429674296842969429704297142972429734297442975429764297742978429794298042981429824298342984429854298642987429884298942990429914299242993429944299542996429974299842999430004300143002430034300443005430064300743008430094301043011430124301343014430154301643017430184301943020430214302243023430244302543026430274302843029430304303143032430334303443035430364303743038430394304043041430424304343044430454304643047430484304943050430514305243053430544305543056430574305843059430604306143062430634306443065430664306743068430694307043071430724307343074430754307643077430784307943080430814308243083430844308543086430874308843089430904309143092430934309443095430964309743098430994310043101431024310343104431054310643107431084310943110431114311243113431144311543116431174311843119431204312143122431234312443125431264312743128431294313043131431324313343134431354313643137431384313943140431414314243143431444314543146431474314843149431504315143152431534315443155431564315743158431594316043161431624316343164431654316643167431684316943170431714317243173431744317543176431774317843179431804318143182431834318443185431864318743188431894319043191431924319343194431954319643197431984319943200432014320243203432044320543206432074320843209432104321143212432134321443215432164321743218432194322043221432224322343224432254322643227432284322943230432314323243233432344323543236432374323843239432404324143242432434324443245432464324743248432494325043251432524325343254432554325643257432584325943260432614326243263432644326543266432674326843269432704327143272432734327443275432764327743278432794328043281432824328343284432854328643287432884328943290432914329243293432944329543296432974329843299433004330143302433034330443305433064330743308433094331043311433124331343314433154331643317433184331943320433214332243323433244332543326433274332843329433304333143332433334333443335433364333743338433394334043341433424334343344433454334643347433484334943350433514335243353433544335543356433574335843359433604336143362433634336443365433664336743368433694337043371433724337343374433754337643377433784337943380433814338243383433844338543386433874338843389433904339143392433934339443395433964339743398433994340043401434024340343404434054340643407434084340943410434114341243413434144341543416434174341843419434204342143422434234342443425434264342743428434294343043431434324343343434434354343643437434384343943440434414344243443434444344543446434474344843449434504345143452434534345443455434564345743458434594346043461434624346343464434654346643467434684346943470434714347243473434744347543476434774347843479434804348143482434834348443485434864348743488434894349043491434924349343494434954349643497434984349943500435014350243503435044350543506435074350843509435104351143512435134351443515435164351743518435194352043521435224352343524435254352643527435284352943530435314353243533435344353543536435374353843539435404354143542435434354443545435464354743548435494355043551435524355343554435554355643557435584355943560435614356243563435644356543566435674356843569435704357143572435734357443575435764357743578435794358043581435824358343584435854358643587435884358943590435914359243593435944359543596435974359843599436004360143602436034360443605436064360743608436094361043611436124361343614436154361643617436184361943620436214362243623436244362543626436274362843629436304363143632436334363443635436364363743638436394364043641436424364343644436454364643647436484364943650436514365243653436544365543656436574365843659436604366143662436634366443665436664366743668436694367043671436724367343674436754367643677436784367943680436814368243683436844368543686436874368843689436904369143692436934369443695436964369743698436994370043701437024370343704437054370643707437084370943710437114371243713437144371543716437174371843719437204372143722437234372443725437264372743728437294373043731437324373343734437354373643737437384373943740437414374243743437444374543746437474374843749437504375143752437534375443755437564375743758437594376043761437624376343764437654376643767437684376943770437714377243773437744377543776437774377843779437804378143782437834378443785437864378743788437894379043791437924379343794437954379643797437984379943800438014380243803438044380543806438074380843809438104381143812438134381443815438164381743818438194382043821438224382343824438254382643827438284382943830438314383243833438344383543836438374383843839438404384143842438434384443845438464384743848438494385043851438524385343854438554385643857438584385943860438614386243863438644386543866438674386843869438704387143872438734387443875438764387743878438794388043881438824388343884438854388643887438884388943890438914389243893438944389543896438974389843899439004390143902439034390443905439064390743908439094391043911439124391343914439154391643917439184391943920439214392243923439244392543926439274392843929439304393143932439334393443935439364393743938439394394043941439424394343944439454394643947439484394943950439514395243953439544395543956439574395843959439604396143962439634396443965439664396743968439694397043971439724397343974439754397643977439784397943980439814398243983439844398543986439874398843989439904399143992439934399443995439964399743998439994400044001440024400344004440054400644007440084400944010440114401244013440144401544016440174401844019440204402144022440234402444025440264402744028440294403044031440324403344034440354403644037440384403944040440414404244043440444404544046440474404844049440504405144052440534405444055440564405744058440594406044061440624406344064440654406644067440684406944070440714407244073440744407544076440774407844079440804408144082440834408444085440864408744088440894409044091440924409344094440954409644097440984409944100441014410244103441044410544106441074410844109441104411144112441134411444115441164411744118441194412044121441224412344124441254412644127441284412944130441314413244133441344413544136441374413844139441404414144142441434414444145441464414744148441494415044151441524415344154441554415644157441584415944160441614416244163441644416544166441674416844169441704417144172441734417444175441764417744178441794418044181441824418344184441854418644187441884418944190441914419244193441944419544196441974419844199442004420144202442034420444205442064420744208442094421044211442124421344214442154421644217442184421944220442214422244223442244422544226442274422844229442304423144232442334423444235442364423744238442394424044241442424424344244442454424644247442484424944250442514425244253442544425544256442574425844259442604426144262442634426444265442664426744268442694427044271442724427344274442754427644277442784427944280442814428244283442844428544286442874428844289442904429144292442934429444295442964429744298442994430044301443024430344304443054430644307443084430944310443114431244313443144431544316443174431844319443204432144322443234432444325443264432744328443294433044331443324433344334443354433644337443384433944340443414434244343443444434544346443474434844349443504435144352443534435444355443564435744358443594436044361443624436344364443654436644367443684436944370443714437244373443744437544376443774437844379443804438144382443834438444385443864438744388443894439044391443924439344394443954439644397443984439944400444014440244403444044440544406444074440844409444104441144412444134441444415444164441744418444194442044421444224442344424444254442644427444284442944430444314443244433444344443544436444374443844439444404444144442444434444444445444464444744448444494445044451444524445344454444554445644457444584445944460444614446244463444644446544466444674446844469444704447144472444734447444475444764447744478444794448044481444824448344484444854448644487444884448944490444914449244493444944449544496444974449844499445004450144502445034450444505445064450744508445094451044511445124451344514445154451644517445184451944520445214452244523445244452544526445274452844529445304453144532445334453444535445364453744538445394454044541445424454344544445454454644547445484454944550445514455244553445544455544556445574455844559445604456144562445634456444565445664456744568445694457044571445724457344574445754457644577445784457944580445814458244583445844458544586445874458844589445904459144592445934459444595445964459744598445994460044601446024460344604446054460644607446084460944610446114461244613446144461544616446174461844619446204462144622446234462444625446264462744628446294463044631446324463344634446354463644637446384463944640446414464244643446444464544646446474464844649446504465144652446534465444655446564465744658446594466044661446624466344664446654466644667446684466944670446714467244673446744467544676446774467844679446804468144682446834468444685446864468744688446894469044691446924469344694446954469644697446984469944700447014470244703447044470544706447074470844709447104471144712447134471444715447164471744718447194472044721447224472344724447254472644727447284472944730447314473244733447344473544736447374473844739447404474144742447434474444745447464474744748447494475044751447524475344754447554475644757447584475944760447614476244763447644476544766447674476844769447704477144772447734477444775447764477744778447794478044781447824478344784447854478644787447884478944790447914479244793447944479544796447974479844799448004480144802448034480444805448064480744808448094481044811448124481344814448154481644817448184481944820448214482244823448244482544826448274482844829448304483144832448334483444835448364483744838448394484044841448424484344844448454484644847448484484944850448514485244853448544485544856448574485844859448604486144862448634486444865448664486744868448694487044871448724487344874448754487644877448784487944880448814488244883448844488544886448874488844889448904489144892448934489444895448964489744898448994490044901449024490344904449054490644907449084490944910449114491244913449144491544916449174491844919449204492144922449234492444925449264492744928449294493044931449324493344934449354493644937449384493944940449414494244943449444494544946449474494844949449504495144952449534495444955449564495744958449594496044961449624496344964449654496644967449684496944970449714497244973449744497544976449774497844979449804498144982449834498444985449864498744988449894499044991449924499344994449954499644997449984499945000450014500245003450044500545006450074500845009450104501145012450134501445015450164501745018450194502045021450224502345024450254502645027450284502945030450314503245033450344503545036450374503845039450404504145042450434504445045450464504745048450494505045051450524505345054450554505645057450584505945060450614506245063450644506545066450674506845069450704507145072450734507445075450764507745078450794508045081450824508345084450854508645087450884508945090450914509245093450944509545096450974509845099451004510145102451034510445105451064510745108451094511045111451124511345114451154511645117451184511945120451214512245123451244512545126451274512845129451304513145132451334513445135451364513745138451394514045141451424514345144451454514645147451484514945150451514515245153451544515545156451574515845159451604516145162451634516445165451664516745168451694517045171451724517345174451754517645177451784517945180451814518245183451844518545186451874518845189451904519145192451934519445195451964519745198451994520045201452024520345204452054520645207452084520945210452114521245213452144521545216452174521845219452204522145222452234522445225452264522745228452294523045231452324523345234452354523645237452384523945240452414524245243452444524545246452474524845249452504525145252452534525445255452564525745258452594526045261452624526345264452654526645267452684526945270452714527245273452744527545276452774527845279452804528145282452834528445285452864528745288452894529045291452924529345294452954529645297452984529945300453014530245303453044530545306453074530845309453104531145312453134531445315453164531745318453194532045321453224532345324453254532645327453284532945330453314533245333453344533545336453374533845339453404534145342453434534445345453464534745348453494535045351453524535345354453554535645357453584535945360453614536245363453644536545366453674536845369453704537145372453734537445375453764537745378453794538045381453824538345384453854538645387453884538945390453914539245393453944539545396453974539845399454004540145402454034540445405454064540745408454094541045411454124541345414454154541645417454184541945420454214542245423454244542545426454274542845429454304543145432454334543445435454364543745438454394544045441454424544345444454454544645447454484544945450454514545245453454544545545456454574545845459454604546145462454634546445465454664546745468454694547045471454724547345474454754547645477454784547945480454814548245483454844548545486454874548845489454904549145492454934549445495454964549745498454994550045501455024550345504455054550645507455084550945510455114551245513455144551545516455174551845519455204552145522455234552445525455264552745528455294553045531455324553345534455354553645537455384553945540455414554245543455444554545546455474554845549455504555145552455534555445555455564555745558455594556045561455624556345564455654556645567455684556945570455714557245573455744557545576455774557845579455804558145582455834558445585455864558745588455894559045591455924559345594455954559645597455984559945600456014560245603456044560545606456074560845609456104561145612456134561445615456164561745618456194562045621456224562345624456254562645627456284562945630456314563245633456344563545636456374563845639456404564145642456434564445645456464564745648456494565045651456524565345654456554565645657456584565945660456614566245663456644566545666456674566845669456704567145672456734567445675456764567745678456794568045681456824568345684456854568645687456884568945690456914569245693456944569545696456974569845699457004570145702457034570445705457064570745708457094571045711457124571345714457154571645717457184571945720457214572245723457244572545726457274572845729457304573145732457334573445735457364573745738457394574045741457424574345744457454574645747457484574945750457514575245753457544575545756457574575845759457604576145762457634576445765457664576745768457694577045771457724577345774457754577645777457784577945780457814578245783457844578545786457874578845789457904579145792457934579445795457964579745798457994580045801458024580345804458054580645807458084580945810458114581245813458144581545816458174581845819458204582145822458234582445825458264582745828458294583045831458324583345834458354583645837458384583945840458414584245843458444584545846458474584845849458504585145852458534585445855458564585745858458594586045861458624586345864458654586645867458684586945870458714587245873458744587545876458774587845879458804588145882458834588445885458864588745888458894589045891458924589345894458954589645897458984589945900459014590245903459044590545906459074590845909459104591145912459134591445915459164591745918459194592045921459224592345924459254592645927459284592945930459314593245933459344593545936459374593845939459404594145942459434594445945459464594745948459494595045951459524595345954459554595645957459584595945960459614596245963459644596545966459674596845969459704597145972459734597445975459764597745978459794598045981459824598345984459854598645987459884598945990459914599245993459944599545996459974599845999460004600146002460034600446005460064600746008460094601046011460124601346014460154601646017460184601946020460214602246023460244602546026460274602846029460304603146032460334603446035460364603746038460394604046041460424604346044460454604646047460484604946050460514605246053460544605546056460574605846059460604606146062460634606446065460664606746068460694607046071460724607346074460754607646077460784607946080460814608246083460844608546086460874608846089460904609146092460934609446095460964609746098460994610046101461024610346104461054610646107461084610946110461114611246113461144611546116461174611846119461204612146122461234612446125461264612746128461294613046131461324613346134461354613646137461384613946140461414614246143461444614546146461474614846149461504615146152461534615446155461564615746158461594616046161461624616346164461654616646167461684616946170461714617246173461744617546176461774617846179461804618146182461834618446185461864618746188461894619046191461924619346194461954619646197461984619946200462014620246203462044620546206462074620846209462104621146212462134621446215462164621746218462194622046221462224622346224462254622646227462284622946230462314623246233462344623546236462374623846239462404624146242462434624446245462464624746248462494625046251462524625346254462554625646257462584625946260462614626246263462644626546266462674626846269462704627146272462734627446275462764627746278462794628046281462824628346284462854628646287462884628946290462914629246293462944629546296462974629846299463004630146302463034630446305463064630746308463094631046311463124631346314463154631646317463184631946320463214632246323463244632546326463274632846329463304633146332463334633446335463364633746338463394634046341463424634346344463454634646347463484634946350463514635246353463544635546356463574635846359463604636146362463634636446365463664636746368463694637046371463724637346374463754637646377463784637946380463814638246383463844638546386463874638846389463904639146392463934639446395463964639746398463994640046401464024640346404464054640646407464084640946410464114641246413464144641546416464174641846419464204642146422464234642446425464264642746428464294643046431464324643346434464354643646437464384643946440464414644246443464444644546446464474644846449464504645146452464534645446455464564645746458464594646046461464624646346464464654646646467464684646946470464714647246473464744647546476464774647846479464804648146482464834648446485464864648746488464894649046491464924649346494464954649646497464984649946500465014650246503465044650546506465074650846509465104651146512465134651446515465164651746518465194652046521465224652346524465254652646527465284652946530465314653246533465344653546536465374653846539465404654146542465434654446545465464654746548465494655046551465524655346554465554655646557465584655946560465614656246563465644656546566465674656846569465704657146572465734657446575465764657746578465794658046581465824658346584465854658646587465884658946590465914659246593465944659546596465974659846599466004660146602466034660446605466064660746608466094661046611466124661346614466154661646617466184661946620466214662246623466244662546626466274662846629466304663146632466334663446635466364663746638466394664046641466424664346644466454664646647466484664946650466514665246653466544665546656466574665846659466604666146662466634666446665466664666746668466694667046671466724667346674466754667646677466784667946680466814668246683466844668546686466874668846689466904669146692466934669446695466964669746698466994670046701467024670346704467054670646707467084670946710467114671246713467144671546716467174671846719467204672146722467234672446725467264672746728467294673046731467324673346734467354673646737467384673946740467414674246743467444674546746467474674846749467504675146752467534675446755467564675746758467594676046761467624676346764467654676646767467684676946770467714677246773467744677546776467774677846779467804678146782467834678446785467864678746788467894679046791467924679346794467954679646797467984679946800468014680246803468044680546806468074680846809468104681146812468134681446815468164681746818468194682046821468224682346824468254682646827468284682946830468314683246833468344683546836468374683846839468404684146842468434684446845468464684746848468494685046851468524685346854468554685646857468584685946860468614686246863468644686546866468674686846869468704687146872468734687446875468764687746878468794688046881468824688346884468854688646887468884688946890468914689246893468944689546896468974689846899469004690146902469034690446905469064690746908469094691046911469124691346914469154691646917469184691946920469214692246923469244692546926469274692846929469304693146932469334693446935469364693746938469394694046941469424694346944469454694646947469484694946950469514695246953469544695546956469574695846959469604696146962469634696446965469664696746968469694697046971469724697346974469754697646977469784697946980469814698246983469844698546986469874698846989469904699146992469934699446995469964699746998469994700047001470024700347004470054700647007470084700947010470114701247013470144701547016470174701847019470204702147022470234702447025470264702747028470294703047031470324703347034470354703647037470384703947040470414704247043470444704547046470474704847049470504705147052470534705447055470564705747058470594706047061470624706347064470654706647067470684706947070470714707247073470744707547076470774707847079470804708147082470834708447085470864708747088470894709047091470924709347094470954709647097470984709947100471014710247103471044710547106471074710847109471104711147112471134711447115471164711747118471194712047121471224712347124471254712647127471284712947130471314713247133471344713547136471374713847139471404714147142471434714447145471464714747148471494715047151471524715347154471554715647157471584715947160471614716247163471644716547166471674716847169471704717147172471734717447175471764717747178471794718047181471824718347184471854718647187471884718947190471914719247193471944719547196471974719847199472004720147202472034720447205472064720747208472094721047211472124721347214472154721647217472184721947220472214722247223472244722547226472274722847229472304723147232472334723447235472364723747238472394724047241472424724347244472454724647247472484724947250472514725247253472544725547256472574725847259472604726147262472634726447265472664726747268472694727047271472724727347274472754727647277472784727947280472814728247283472844728547286472874728847289472904729147292472934729447295472964729747298472994730047301473024730347304473054730647307473084730947310473114731247313473144731547316473174731847319473204732147322473234732447325473264732747328473294733047331473324733347334473354733647337473384733947340473414734247343473444734547346473474734847349473504735147352473534735447355473564735747358473594736047361473624736347364473654736647367473684736947370473714737247373473744737547376473774737847379473804738147382473834738447385473864738747388473894739047391473924739347394473954739647397473984739947400474014740247403474044740547406474074740847409474104741147412474134741447415474164741747418474194742047421474224742347424474254742647427474284742947430474314743247433474344743547436474374743847439474404744147442474434744447445474464744747448474494745047451474524745347454474554745647457474584745947460474614746247463474644746547466474674746847469474704747147472474734747447475474764747747478474794748047481474824748347484474854748647487474884748947490474914749247493474944749547496474974749847499475004750147502475034750447505475064750747508475094751047511475124751347514475154751647517475184751947520475214752247523475244752547526475274752847529475304753147532475334753447535475364753747538475394754047541475424754347544475454754647547475484754947550475514755247553475544755547556475574755847559475604756147562475634756447565475664756747568475694757047571475724757347574475754757647577475784757947580475814758247583475844758547586475874758847589475904759147592475934759447595475964759747598475994760047601476024760347604476054760647607476084760947610476114761247613476144761547616476174761847619476204762147622476234762447625476264762747628476294763047631476324763347634476354763647637476384763947640476414764247643476444764547646476474764847649476504765147652476534765447655476564765747658476594766047661476624766347664476654766647667476684766947670476714767247673476744767547676476774767847679476804768147682476834768447685476864768747688476894769047691476924769347694476954769647697476984769947700477014770247703477044770547706477074770847709477104771147712477134771447715477164771747718477194772047721477224772347724477254772647727477284772947730477314773247733477344773547736477374773847739477404774147742477434774447745477464774747748477494775047751477524775347754477554775647757477584775947760477614776247763477644776547766477674776847769477704777147772477734777447775477764777747778477794778047781477824778347784477854778647787477884778947790477914779247793477944779547796477974779847799478004780147802478034780447805478064780747808478094781047811478124781347814478154781647817478184781947820478214782247823478244782547826478274782847829478304783147832478334783447835478364783747838478394784047841478424784347844478454784647847478484784947850478514785247853478544785547856478574785847859478604786147862478634786447865478664786747868478694787047871478724787347874478754787647877478784787947880478814788247883478844788547886478874788847889478904789147892478934789447895478964789747898478994790047901479024790347904479054790647907479084790947910479114791247913479144791547916479174791847919479204792147922479234792447925479264792747928479294793047931479324793347934479354793647937479384793947940479414794247943479444794547946479474794847949479504795147952479534795447955479564795747958479594796047961479624796347964479654796647967479684796947970479714797247973479744797547976479774797847979479804798147982479834798447985479864798747988479894799047991479924799347994479954799647997479984799948000480014800248003480044800548006480074800848009480104801148012480134801448015480164801748018480194802048021480224802348024480254802648027480284802948030480314803248033480344803548036480374803848039480404804148042480434804448045480464804748048480494805048051480524805348054480554805648057480584805948060480614806248063480644806548066480674806848069480704807148072480734807448075480764807748078480794808048081480824808348084480854808648087480884808948090480914809248093480944809548096480974809848099481004810148102481034810448105481064810748108481094811048111481124811348114481154811648117481184811948120481214812248123481244812548126481274812848129481304813148132481334813448135481364813748138481394814048141481424814348144481454814648147481484814948150481514815248153481544815548156481574815848159481604816148162481634816448165481664816748168481694817048171481724817348174481754817648177481784817948180481814818248183481844818548186481874818848189481904819148192481934819448195481964819748198481994820048201482024820348204482054820648207482084820948210482114821248213482144821548216482174821848219482204822148222482234822448225482264822748228482294823048231482324823348234482354823648237482384823948240482414824248243482444824548246482474824848249482504825148252482534825448255482564825748258482594826048261482624826348264482654826648267482684826948270482714827248273482744827548276482774827848279482804828148282482834828448285482864828748288482894829048291482924829348294482954829648297482984829948300483014830248303483044830548306483074830848309483104831148312483134831448315483164831748318483194832048321483224832348324483254832648327483284832948330483314833248333483344833548336483374833848339483404834148342483434834448345483464834748348483494835048351483524835348354483554835648357483584835948360483614836248363483644836548366483674836848369483704837148372483734837448375483764837748378483794838048381483824838348384483854838648387483884838948390483914839248393483944839548396483974839848399484004840148402484034840448405484064840748408484094841048411484124841348414484154841648417484184841948420484214842248423484244842548426484274842848429484304843148432484334843448435484364843748438484394844048441484424844348444484454844648447484484844948450484514845248453484544845548456484574845848459484604846148462484634846448465484664846748468484694847048471484724847348474484754847648477484784847948480484814848248483484844848548486484874848848489484904849148492484934849448495484964849748498484994850048501485024850348504485054850648507485084850948510485114851248513485144851548516485174851848519485204852148522485234852448525485264852748528485294853048531485324853348534485354853648537485384853948540485414854248543485444854548546485474854848549485504855148552485534855448555485564855748558485594856048561485624856348564485654856648567485684856948570485714857248573485744857548576485774857848579485804858148582485834858448585485864858748588485894859048591485924859348594485954859648597485984859948600486014860248603486044860548606486074860848609486104861148612486134861448615486164861748618486194862048621486224862348624486254862648627486284862948630486314863248633486344863548636486374863848639486404864148642486434864448645486464864748648486494865048651486524865348654486554865648657486584865948660486614866248663486644866548666486674866848669486704867148672486734867448675486764867748678486794868048681486824868348684486854868648687486884868948690486914869248693486944869548696486974869848699487004870148702487034870448705487064870748708487094871048711487124871348714487154871648717487184871948720487214872248723487244872548726487274872848729487304873148732487334873448735487364873748738487394874048741487424874348744487454874648747487484874948750487514875248753487544875548756487574875848759487604876148762487634876448765487664876748768487694877048771487724877348774487754877648777487784877948780487814878248783487844878548786487874878848789487904879148792487934879448795487964879748798487994880048801488024880348804488054880648807488084880948810488114881248813488144881548816488174881848819488204882148822488234882448825488264882748828488294883048831488324883348834488354883648837488384883948840488414884248843488444884548846488474884848849488504885148852488534885448855488564885748858488594886048861488624886348864488654886648867488684886948870488714887248873488744887548876488774887848879488804888148882488834888448885488864888748888488894889048891488924889348894488954889648897488984889948900489014890248903489044890548906489074890848909489104891148912489134891448915489164891748918489194892048921489224892348924489254892648927489284892948930489314893248933489344893548936489374893848939489404894148942489434894448945489464894748948489494895048951489524895348954489554895648957489584895948960489614896248963489644896548966489674896848969489704897148972489734897448975489764897748978489794898048981489824898348984489854898648987489884898948990489914899248993489944899548996489974899848999490004900149002490034900449005490064900749008490094901049011490124901349014490154901649017490184901949020490214902249023490244902549026490274902849029490304903149032490334903449035490364903749038490394904049041490424904349044490454904649047490484904949050490514905249053490544905549056490574905849059490604906149062490634906449065490664906749068490694907049071490724907349074490754907649077490784907949080490814908249083490844908549086490874908849089490904909149092490934909449095490964909749098490994910049101491024910349104491054910649107491084910949110491114911249113491144911549116491174911849119491204912149122491234912449125491264912749128491294913049131491324913349134491354913649137491384913949140491414914249143491444914549146491474914849149491504915149152491534915449155491564915749158491594916049161491624916349164491654916649167491684916949170491714917249173491744917549176491774917849179491804918149182491834918449185491864918749188491894919049191491924919349194491954919649197491984919949200492014920249203492044920549206492074920849209492104921149212492134921449215492164921749218492194922049221492224922349224492254922649227492284922949230492314923249233492344923549236492374923849239492404924149242492434924449245492464924749248492494925049251492524925349254492554925649257492584925949260492614926249263492644926549266492674926849269492704927149272492734927449275492764927749278492794928049281492824928349284492854928649287492884928949290492914929249293492944929549296492974929849299493004930149302493034930449305493064930749308493094931049311493124931349314493154931649317493184931949320493214932249323493244932549326493274932849329493304933149332493334933449335493364933749338493394934049341493424934349344493454934649347493484934949350493514935249353493544935549356493574935849359493604936149362493634936449365493664936749368493694937049371493724937349374493754937649377493784937949380493814938249383493844938549386493874938849389493904939149392493934939449395493964939749398493994940049401494024940349404494054940649407494084940949410494114941249413494144941549416494174941849419494204942149422494234942449425494264942749428494294943049431494324943349434494354943649437494384943949440494414944249443494444944549446494474944849449494504945149452494534945449455494564945749458494594946049461494624946349464494654946649467494684946949470494714947249473494744947549476494774947849479494804948149482494834948449485494864948749488494894949049491494924949349494494954949649497494984949949500495014950249503495044950549506495074950849509495104951149512495134951449515495164951749518495194952049521495224952349524495254952649527495284952949530495314953249533495344953549536495374953849539495404954149542495434954449545495464954749548495494955049551495524955349554495554955649557495584955949560495614956249563495644956549566495674956849569495704957149572495734957449575495764957749578495794958049581495824958349584495854958649587495884958949590495914959249593495944959549596495974959849599496004960149602496034960449605496064960749608496094961049611496124961349614496154961649617496184961949620496214962249623496244962549626496274962849629496304963149632496334963449635496364963749638496394964049641496424964349644496454964649647496484964949650496514965249653496544965549656496574965849659496604966149662496634966449665496664966749668496694967049671496724967349674496754967649677496784967949680496814968249683496844968549686496874968849689496904969149692496934969449695496964969749698496994970049701497024970349704497054970649707497084970949710497114971249713497144971549716497174971849719497204972149722497234972449725497264972749728497294973049731497324973349734497354973649737497384973949740497414974249743497444974549746497474974849749497504975149752497534975449755497564975749758497594976049761497624976349764497654976649767497684976949770497714977249773497744977549776497774977849779497804978149782497834978449785497864978749788497894979049791497924979349794497954979649797497984979949800498014980249803498044980549806498074980849809498104981149812498134981449815498164981749818498194982049821498224982349824498254982649827498284982949830498314983249833498344983549836498374983849839498404984149842498434984449845498464984749848498494985049851498524985349854498554985649857498584985949860498614986249863498644986549866498674986849869498704987149872498734987449875498764987749878498794988049881498824988349884498854988649887498884988949890498914989249893498944989549896498974989849899499004990149902499034990449905499064990749908499094991049911499124991349914499154991649917499184991949920499214992249923499244992549926499274992849929499304993149932499334993449935499364993749938499394994049941499424994349944499454994649947499484994949950499514995249953499544995549956499574995849959499604996149962499634996449965499664996749968499694997049971499724997349974499754997649977499784997949980499814998249983499844998549986499874998849989499904999149992499934999449995499964999749998499995000050001500025000350004500055000650007500085000950010500115001250013500145001550016500175001850019500205002150022500235002450025500265002750028500295003050031500325003350034500355003650037500385003950040500415004250043500445004550046500475004850049500505005150052500535005450055500565005750058500595006050061500625006350064500655006650067500685006950070500715007250073500745007550076500775007850079500805008150082500835008450085500865008750088500895009050091500925009350094500955009650097500985009950100501015010250103501045010550106501075010850109501105011150112501135011450115501165011750118501195012050121501225012350124501255012650127501285012950130501315013250133501345013550136501375013850139501405014150142501435014450145501465014750148501495015050151501525015350154501555015650157501585015950160501615016250163501645016550166501675016850169501705017150172501735017450175501765017750178501795018050181501825018350184501855018650187501885018950190501915019250193501945019550196501975019850199502005020150202502035020450205502065020750208502095021050211502125021350214502155021650217502185021950220502215022250223502245022550226502275022850229502305023150232502335023450235502365023750238502395024050241502425024350244502455024650247502485024950250502515025250253502545025550256502575025850259502605026150262502635026450265502665026750268502695027050271502725027350274502755027650277502785027950280502815028250283502845028550286502875028850289502905029150292502935029450295502965029750298502995030050301503025030350304503055030650307503085030950310503115031250313503145031550316503175031850319503205032150322503235032450325503265032750328503295033050331503325033350334503355033650337503385033950340503415034250343503445034550346503475034850349503505035150352503535035450355503565035750358503595036050361503625036350364503655036650367503685036950370503715037250373503745037550376503775037850379503805038150382503835038450385503865038750388503895039050391503925039350394503955039650397503985039950400504015040250403504045040550406504075040850409504105041150412504135041450415504165041750418504195042050421504225042350424504255042650427504285042950430504315043250433504345043550436504375043850439504405044150442504435044450445504465044750448504495045050451504525045350454504555045650457504585045950460504615046250463504645046550466504675046850469504705047150472504735047450475504765047750478504795048050481504825048350484504855048650487504885048950490504915049250493504945049550496504975049850499505005050150502505035050450505505065050750508505095051050511505125051350514505155051650517505185051950520505215052250523505245052550526505275052850529505305053150532505335053450535505365053750538505395054050541505425054350544505455054650547505485054950550505515055250553505545055550556505575055850559505605056150562505635056450565505665056750568505695057050571505725057350574505755057650577505785057950580505815058250583505845058550586505875058850589505905059150592505935059450595505965059750598505995060050601506025060350604506055060650607506085060950610506115061250613506145061550616506175061850619506205062150622506235062450625506265062750628506295063050631506325063350634506355063650637506385063950640506415064250643506445064550646506475064850649506505065150652506535065450655506565065750658506595066050661506625066350664506655066650667506685066950670506715067250673506745067550676506775067850679506805068150682506835068450685506865068750688506895069050691506925069350694506955069650697506985069950700507015070250703507045070550706507075070850709507105071150712507135071450715507165071750718507195072050721507225072350724507255072650727507285072950730507315073250733507345073550736507375073850739507405074150742507435074450745507465074750748507495075050751507525075350754507555075650757507585075950760507615076250763507645076550766507675076850769507705077150772507735077450775507765077750778507795078050781507825078350784507855078650787507885078950790507915079250793507945079550796507975079850799508005080150802508035080450805508065080750808508095081050811508125081350814508155081650817508185081950820508215082250823508245082550826508275082850829508305083150832508335083450835508365083750838508395084050841508425084350844508455084650847508485084950850508515085250853508545085550856508575085850859508605086150862508635086450865508665086750868508695087050871508725087350874508755087650877508785087950880508815088250883508845088550886508875088850889508905089150892508935089450895508965089750898508995090050901509025090350904509055090650907509085090950910509115091250913509145091550916509175091850919509205092150922509235092450925509265092750928509295093050931509325093350934509355093650937509385093950940509415094250943509445094550946509475094850949509505095150952509535095450955509565095750958509595096050961509625096350964509655096650967509685096950970509715097250973509745097550976509775097850979509805098150982509835098450985509865098750988509895099050991509925099350994509955099650997509985099951000510015100251003510045100551006510075100851009510105101151012510135101451015510165101751018510195102051021510225102351024510255102651027510285102951030510315103251033510345103551036510375103851039510405104151042510435104451045510465104751048510495105051051510525105351054510555105651057510585105951060510615106251063510645106551066510675106851069510705107151072510735107451075510765107751078510795108051081510825108351084510855108651087510885108951090510915109251093510945109551096510975109851099511005110151102511035110451105511065110751108511095111051111511125111351114511155111651117511185111951120511215112251123511245112551126511275112851129511305113151132511335113451135511365113751138511395114051141511425114351144511455114651147511485114951150511515115251153511545115551156511575115851159511605116151162511635116451165511665116751168511695117051171511725117351174511755117651177511785117951180511815118251183511845118551186511875118851189511905119151192511935119451195511965119751198511995120051201512025120351204512055120651207512085120951210512115121251213512145121551216512175121851219512205122151222512235122451225512265122751228512295123051231512325123351234512355123651237512385123951240512415124251243512445124551246512475124851249512505125151252512535125451255512565125751258512595126051261512625126351264512655126651267512685126951270512715127251273512745127551276512775127851279512805128151282512835128451285512865128751288512895129051291512925129351294512955129651297512985129951300513015130251303513045130551306513075130851309513105131151312513135131451315513165131751318513195132051321513225132351324513255132651327513285132951330513315133251333513345133551336513375133851339513405134151342513435134451345513465134751348513495135051351513525135351354513555135651357513585135951360513615136251363513645136551366513675136851369513705137151372513735137451375513765137751378513795138051381513825138351384513855138651387513885138951390513915139251393513945139551396513975139851399514005140151402514035140451405514065140751408514095141051411514125141351414514155141651417514185141951420514215142251423514245142551426514275142851429514305143151432514335143451435514365143751438514395144051441514425144351444514455144651447514485144951450514515145251453514545145551456514575145851459514605146151462514635146451465514665146751468514695147051471514725147351474514755147651477514785147951480514815148251483514845148551486514875148851489514905149151492514935149451495514965149751498514995150051501515025150351504515055150651507515085150951510515115151251513515145151551516515175151851519515205152151522515235152451525515265152751528515295153051531515325153351534515355153651537515385153951540515415154251543515445154551546515475154851549515505155151552515535155451555515565155751558515595156051561515625156351564515655156651567515685156951570515715157251573515745157551576515775157851579515805158151582515835158451585515865158751588515895159051591515925159351594515955159651597515985159951600516015160251603516045160551606516075160851609516105161151612516135161451615516165161751618516195162051621516225162351624516255162651627516285162951630516315163251633516345163551636516375163851639516405164151642516435164451645516465164751648516495165051651516525165351654516555165651657516585165951660516615166251663516645166551666516675166851669516705167151672516735167451675516765167751678516795168051681516825168351684516855168651687516885168951690516915169251693516945169551696516975169851699517005170151702517035170451705517065170751708517095171051711517125171351714517155171651717517185171951720517215172251723517245172551726517275172851729517305173151732517335173451735517365173751738517395174051741517425174351744517455174651747517485174951750517515175251753517545175551756517575175851759517605176151762517635176451765517665176751768517695177051771517725177351774517755177651777517785177951780517815178251783517845178551786517875178851789517905179151792517935179451795517965179751798517995180051801518025180351804518055180651807518085180951810518115181251813518145181551816518175181851819518205182151822518235182451825518265182751828518295183051831518325183351834518355183651837518385183951840518415184251843518445184551846518475184851849518505185151852518535185451855518565185751858518595186051861518625186351864518655186651867518685186951870518715187251873518745187551876518775187851879518805188151882518835188451885518865188751888518895189051891518925189351894518955189651897518985189951900519015190251903519045190551906519075190851909519105191151912519135191451915519165191751918519195192051921519225192351924519255192651927519285192951930519315193251933519345193551936519375193851939519405194151942519435194451945519465194751948519495195051951519525195351954519555195651957519585195951960519615196251963519645196551966519675196851969519705197151972519735197451975519765197751978519795198051981519825198351984519855198651987519885198951990519915199251993519945199551996519975199851999520005200152002520035200452005520065200752008520095201052011520125201352014520155201652017520185201952020520215202252023520245202552026520275202852029520305203152032520335203452035520365203752038520395204052041520425204352044520455204652047520485204952050520515205252053520545205552056520575205852059520605206152062520635206452065520665206752068520695207052071520725207352074520755207652077520785207952080520815208252083520845208552086520875208852089520905209152092520935209452095520965209752098520995210052101521025210352104521055210652107521085210952110521115211252113521145211552116521175211852119521205212152122521235212452125521265212752128521295213052131521325213352134521355213652137521385213952140521415214252143521445214552146521475214852149521505215152152521535215452155521565215752158521595216052161521625216352164521655216652167521685216952170521715217252173521745217552176521775217852179521805218152182521835218452185521865218752188521895219052191521925219352194521955219652197521985219952200522015220252203522045220552206522075220852209522105221152212522135221452215522165221752218522195222052221522225222352224522255222652227522285222952230522315223252233522345223552236522375223852239522405224152242522435224452245522465224752248522495225052251522525225352254522555225652257522585225952260522615226252263522645226552266522675226852269522705227152272522735227452275522765227752278522795228052281522825228352284522855228652287522885228952290522915229252293522945229552296522975229852299523005230152302523035230452305523065230752308523095231052311523125231352314523155231652317523185231952320523215232252323523245232552326523275232852329523305233152332523335233452335523365233752338523395234052341523425234352344523455234652347523485234952350523515235252353523545235552356523575235852359523605236152362523635236452365523665236752368523695237052371523725237352374523755237652377523785237952380523815238252383523845238552386523875238852389523905239152392523935239452395523965239752398523995240052401524025240352404524055240652407524085240952410524115241252413524145241552416524175241852419524205242152422524235242452425524265242752428524295243052431524325243352434524355243652437524385243952440524415244252443524445244552446524475244852449524505245152452524535245452455524565245752458524595246052461524625246352464524655246652467524685246952470524715247252473524745247552476524775247852479524805248152482524835248452485524865248752488524895249052491524925249352494524955249652497524985249952500525015250252503525045250552506525075250852509525105251152512525135251452515525165251752518525195252052521525225252352524525255252652527525285252952530525315253252533525345253552536525375253852539525405254152542525435254452545525465254752548525495255052551525525255352554525555255652557525585255952560525615256252563525645256552566525675256852569525705257152572525735257452575525765257752578525795258052581525825258352584525855258652587525885258952590525915259252593525945259552596525975259852599526005260152602526035260452605526065260752608526095261052611526125261352614526155261652617526185261952620526215262252623526245262552626526275262852629526305263152632526335263452635526365263752638526395264052641526425264352644526455264652647526485264952650526515265252653526545265552656526575265852659526605266152662526635266452665526665266752668526695267052671526725267352674526755267652677526785267952680526815268252683526845268552686526875268852689526905269152692526935269452695526965269752698526995270052701527025270352704527055270652707527085270952710527115271252713527145271552716527175271852719527205272152722527235272452725527265272752728527295273052731527325273352734527355273652737527385273952740527415274252743527445274552746527475274852749527505275152752527535275452755527565275752758527595276052761527625276352764527655276652767527685276952770527715277252773527745277552776527775277852779527805278152782527835278452785527865278752788527895279052791527925279352794527955279652797527985279952800528015280252803528045280552806528075280852809528105281152812528135281452815528165281752818528195282052821528225282352824528255282652827528285282952830528315283252833528345283552836528375283852839528405284152842528435284452845528465284752848528495285052851528525285352854528555285652857528585285952860528615286252863528645286552866528675286852869528705287152872528735287452875528765287752878528795288052881528825288352884528855288652887528885288952890528915289252893528945289552896528975289852899529005290152902529035290452905529065290752908529095291052911529125291352914529155291652917529185291952920529215292252923529245292552926529275292852929529305293152932529335293452935529365293752938529395294052941529425294352944529455294652947529485294952950529515295252953529545295552956529575295852959529605296152962529635296452965529665296752968529695297052971529725297352974529755297652977529785297952980529815298252983529845298552986529875298852989529905299152992529935299452995529965299752998529995300053001530025300353004530055300653007530085300953010530115301253013530145301553016530175301853019530205302153022530235302453025530265302753028530295303053031530325303353034530355303653037530385303953040530415304253043530445304553046530475304853049530505305153052530535305453055530565305753058530595306053061530625306353064530655306653067530685306953070530715307253073530745307553076530775307853079530805308153082530835308453085530865308753088530895309053091530925309353094530955309653097530985309953100531015310253103531045310553106531075310853109531105311153112531135311453115531165311753118531195312053121531225312353124531255312653127531285312953130531315313253133531345313553136531375313853139531405314153142531435314453145531465314753148531495315053151531525315353154531555315653157531585315953160531615316253163531645316553166531675316853169531705317153172531735317453175531765317753178531795318053181531825318353184531855318653187531885318953190531915319253193531945319553196531975319853199532005320153202532035320453205532065320753208532095321053211532125321353214532155321653217532185321953220532215322253223532245322553226532275322853229532305323153232532335323453235532365323753238532395324053241532425324353244532455324653247532485324953250532515325253253532545325553256532575325853259532605326153262532635326453265532665326753268532695327053271532725327353274532755327653277532785327953280532815328253283532845328553286532875328853289532905329153292532935329453295532965329753298532995330053301533025330353304533055330653307533085330953310533115331253313533145331553316533175331853319533205332153322533235332453325533265332753328533295333053331533325333353334533355333653337533385333953340533415334253343533445334553346533475334853349533505335153352533535335453355533565335753358533595336053361533625336353364533655336653367533685336953370533715337253373533745337553376533775337853379533805338153382533835338453385533865338753388533895339053391533925339353394533955339653397533985339953400534015340253403534045340553406534075340853409534105341153412534135341453415534165341753418534195342053421534225342353424534255342653427534285342953430534315343253433534345343553436534375343853439534405344153442534435344453445534465344753448534495345053451534525345353454534555345653457534585345953460534615346253463534645346553466534675346853469534705347153472534735347453475534765347753478534795348053481534825348353484534855348653487534885348953490534915349253493534945349553496534975349853499535005350153502535035350453505535065350753508535095351053511535125351353514535155351653517535185351953520535215352253523535245352553526535275352853529535305353153532535335353453535535365353753538535395354053541535425354353544535455354653547535485354953550535515355253553535545355553556535575355853559535605356153562535635356453565535665356753568535695357053571535725357353574535755357653577535785357953580535815358253583535845358553586535875358853589535905359153592535935359453595535965359753598535995360053601536025360353604536055360653607536085360953610536115361253613536145361553616536175361853619536205362153622536235362453625536265362753628536295363053631536325363353634536355363653637536385363953640536415364253643536445364553646536475364853649536505365153652536535365453655536565365753658536595366053661536625366353664536655366653667536685366953670536715367253673536745367553676536775367853679536805368153682536835368453685536865368753688536895369053691536925369353694536955369653697536985369953700537015370253703537045370553706537075370853709537105371153712537135371453715537165371753718537195372053721537225372353724537255372653727537285372953730537315373253733537345373553736537375373853739537405374153742537435374453745537465374753748537495375053751537525375353754537555375653757537585375953760537615376253763537645376553766537675376853769537705377153772537735377453775537765377753778537795378053781537825378353784537855378653787537885378953790537915379253793537945379553796537975379853799538005380153802538035380453805538065380753808538095381053811538125381353814538155381653817538185381953820538215382253823538245382553826538275382853829538305383153832538335383453835538365383753838538395384053841538425384353844538455384653847538485384953850538515385253853538545385553856538575385853859538605386153862538635386453865538665386753868538695387053871538725387353874538755387653877538785387953880538815388253883538845388553886538875388853889538905389153892538935389453895538965389753898538995390053901539025390353904539055390653907539085390953910539115391253913539145391553916539175391853919539205392153922539235392453925539265392753928539295393053931539325393353934539355393653937539385393953940539415394253943539445394553946539475394853949539505395153952539535395453955539565395753958539595396053961539625396353964539655396653967539685396953970539715397253973539745397553976539775397853979539805398153982539835398453985539865398753988539895399053991539925399353994539955399653997539985399954000540015400254003540045400554006540075400854009540105401154012540135401454015540165401754018540195402054021540225402354024540255402654027540285402954030540315403254033540345403554036540375403854039540405404154042540435404454045540465404754048540495405054051540525405354054540555405654057540585405954060540615406254063540645406554066540675406854069540705407154072540735407454075540765407754078540795408054081540825408354084540855408654087540885408954090540915409254093540945409554096540975409854099541005410154102541035410454105541065410754108541095411054111541125411354114541155411654117541185411954120541215412254123541245412554126541275412854129541305413154132541335413454135541365413754138541395414054141541425414354144541455414654147541485414954150541515415254153541545415554156541575415854159541605416154162541635416454165541665416754168541695417054171541725417354174541755417654177541785417954180541815418254183541845418554186541875418854189541905419154192541935419454195541965419754198541995420054201542025420354204542055420654207542085420954210542115421254213542145421554216542175421854219542205422154222542235422454225542265422754228542295423054231542325423354234542355423654237542385423954240542415424254243542445424554246542475424854249542505425154252542535425454255542565425754258542595426054261542625426354264542655426654267542685426954270542715427254273542745427554276542775427854279542805428154282542835428454285542865428754288542895429054291542925429354294542955429654297542985429954300543015430254303543045430554306543075430854309543105431154312543135431454315543165431754318543195432054321543225432354324543255432654327543285432954330543315433254333543345433554336543375433854339543405434154342543435434454345543465434754348543495435054351543525435354354543555435654357543585435954360543615436254363543645436554366543675436854369543705437154372543735437454375543765437754378543795438054381543825438354384543855438654387543885438954390543915439254393543945439554396543975439854399544005440154402544035440454405544065440754408544095441054411544125441354414544155441654417544185441954420544215442254423544245442554426544275442854429544305443154432544335443454435544365443754438544395444054441544425444354444544455444654447544485444954450544515445254453544545445554456544575445854459544605446154462544635446454465544665446754468544695447054471544725447354474544755447654477544785447954480544815448254483544845448554486544875448854489544905449154492544935449454495544965449754498544995450054501545025450354504545055450654507545085450954510545115451254513545145451554516545175451854519545205452154522545235452454525545265452754528545295453054531545325453354534545355453654537545385453954540545415454254543545445454554546545475454854549545505455154552545535455454555545565455754558545595456054561545625456354564545655456654567545685456954570545715457254573545745457554576545775457854579545805458154582545835458454585545865458754588545895459054591545925459354594545955459654597545985459954600546015460254603546045460554606546075460854609546105461154612546135461454615546165461754618546195462054621546225462354624546255462654627546285462954630546315463254633546345463554636546375463854639546405464154642546435464454645546465464754648546495465054651546525465354654546555465654657546585465954660546615466254663546645466554666546675466854669546705467154672546735467454675546765467754678546795468054681546825468354684546855468654687546885468954690546915469254693546945469554696546975469854699547005470154702547035470454705547065470754708547095471054711547125471354714547155471654717547185471954720547215472254723547245472554726547275472854729547305473154732547335473454735547365473754738547395474054741547425474354744547455474654747547485474954750547515475254753547545475554756547575475854759547605476154762547635476454765547665476754768547695477054771547725477354774547755477654777547785477954780547815478254783547845478554786547875478854789547905479154792547935479454795547965479754798547995480054801548025480354804548055480654807548085480954810548115481254813548145481554816548175481854819548205482154822548235482454825548265482754828548295483054831548325483354834548355483654837548385483954840548415484254843548445484554846548475484854849548505485154852548535485454855548565485754858548595486054861548625486354864548655486654867548685486954870548715487254873548745487554876548775487854879548805488154882548835488454885548865488754888548895489054891548925489354894548955489654897548985489954900549015490254903549045490554906549075490854909549105491154912549135491454915549165491754918549195492054921549225492354924549255492654927549285492954930549315493254933549345493554936549375493854939549405494154942549435494454945549465494754948549495495054951549525495354954549555495654957549585495954960549615496254963549645496554966549675496854969549705497154972549735497454975549765497754978549795498054981549825498354984549855498654987549885498954990549915499254993549945499554996549975499854999550005500155002550035500455005550065500755008550095501055011550125501355014550155501655017550185501955020550215502255023550245502555026550275502855029550305503155032550335503455035550365503755038550395504055041550425504355044550455504655047550485504955050550515505255053550545505555056550575505855059550605506155062550635506455065550665506755068550695507055071550725507355074550755507655077550785507955080550815508255083550845508555086550875508855089550905509155092550935509455095550965509755098550995510055101551025510355104551055510655107551085510955110551115511255113551145511555116551175511855119551205512155122551235512455125551265512755128551295513055131551325513355134551355513655137551385513955140551415514255143551445514555146551475514855149551505515155152551535515455155551565515755158551595516055161551625516355164551655516655167551685516955170551715517255173551745517555176551775517855179551805518155182551835518455185551865518755188551895519055191551925519355194551955519655197551985519955200552015520255203552045520555206552075520855209552105521155212552135521455215552165521755218552195522055221552225522355224552255522655227552285522955230552315523255233552345523555236552375523855239552405524155242552435524455245552465524755248552495525055251552525525355254552555525655257552585525955260552615526255263552645526555266552675526855269552705527155272552735527455275552765527755278552795528055281552825528355284552855528655287552885528955290552915529255293552945529555296552975529855299553005530155302553035530455305553065530755308553095531055311553125531355314553155531655317553185531955320553215532255323553245532555326553275532855329553305533155332553335533455335553365533755338553395534055341553425534355344553455534655347553485534955350553515535255353553545535555356553575535855359553605536155362553635536455365553665536755368553695537055371553725537355374553755537655377553785537955380553815538255383553845538555386553875538855389553905539155392553935539455395553965539755398553995540055401554025540355404554055540655407554085540955410554115541255413554145541555416554175541855419554205542155422554235542455425554265542755428554295543055431554325543355434554355543655437554385543955440554415544255443554445544555446554475544855449554505545155452554535545455455554565545755458554595546055461554625546355464554655546655467554685546955470554715547255473554745547555476554775547855479554805548155482554835548455485554865548755488554895549055491554925549355494554955549655497554985549955500555015550255503555045550555506555075550855509555105551155512555135551455515555165551755518555195552055521555225552355524555255552655527555285552955530555315553255533555345553555536555375553855539555405554155542555435554455545555465554755548555495555055551555525555355554555555555655557555585555955560555615556255563555645556555566555675556855569555705557155572555735557455575555765557755578555795558055581555825558355584555855558655587555885558955590555915559255593555945559555596555975559855599556005560155602556035560455605556065560755608556095561055611556125561355614556155561655617556185561955620556215562255623556245562555626556275562855629556305563155632556335563455635556365563755638556395564055641556425564355644556455564655647556485564955650556515565255653556545565555656556575565855659556605566155662556635566455665556665566755668556695567055671556725567355674556755567655677556785567955680556815568255683556845568555686556875568855689556905569155692556935569455695556965569755698556995570055701557025570355704557055570655707557085570955710557115571255713557145571555716557175571855719557205572155722557235572455725557265572755728557295573055731557325573355734557355573655737557385573955740557415574255743557445574555746557475574855749557505575155752557535575455755557565575755758557595576055761557625576355764557655576655767557685576955770557715577255773557745577555776557775577855779557805578155782557835578455785557865578755788557895579055791557925579355794557955579655797557985579955800558015580255803558045580555806558075580855809558105581155812558135581455815558165581755818558195582055821558225582355824558255582655827558285582955830558315583255833558345583555836558375583855839558405584155842558435584455845558465584755848558495585055851558525585355854558555585655857558585585955860558615586255863558645586555866558675586855869558705587155872558735587455875558765587755878558795588055881558825588355884558855588655887558885588955890558915589255893558945589555896558975589855899559005590155902559035590455905559065590755908559095591055911559125591355914559155591655917559185591955920559215592255923559245592555926559275592855929559305593155932559335593455935559365593755938559395594055941559425594355944559455594655947559485594955950559515595255953559545595555956559575595855959559605596155962559635596455965559665596755968559695597055971559725597355974559755597655977559785597955980559815598255983559845598555986559875598855989559905599155992559935599455995559965599755998559995600056001560025600356004560055600656007560085600956010560115601256013560145601556016560175601856019560205602156022560235602456025560265602756028560295603056031560325603356034560355603656037560385603956040560415604256043560445604556046560475604856049560505605156052560535605456055560565605756058560595606056061560625606356064560655606656067560685606956070560715607256073560745607556076560775607856079560805608156082560835608456085560865608756088560895609056091560925609356094560955609656097560985609956100561015610256103561045610556106561075610856109561105611156112561135611456115561165611756118561195612056121561225612356124561255612656127561285612956130561315613256133561345613556136561375613856139561405614156142561435614456145561465614756148561495615056151561525615356154561555615656157561585615956160561615616256163561645616556166561675616856169561705617156172561735617456175561765617756178561795618056181561825618356184561855618656187561885618956190561915619256193561945619556196561975619856199562005620156202562035620456205562065620756208562095621056211562125621356214562155621656217562185621956220562215622256223562245622556226562275622856229562305623156232562335623456235562365623756238562395624056241562425624356244562455624656247562485624956250562515625256253562545625556256562575625856259562605626156262562635626456265562665626756268562695627056271562725627356274562755627656277562785627956280562815628256283562845628556286562875628856289562905629156292562935629456295562965629756298562995630056301563025630356304563055630656307563085630956310563115631256313563145631556316563175631856319563205632156322563235632456325563265632756328563295633056331563325633356334563355633656337563385633956340563415634256343563445634556346563475634856349563505635156352563535635456355563565635756358563595636056361563625636356364563655636656367563685636956370563715637256373563745637556376563775637856379563805638156382563835638456385563865638756388563895639056391563925639356394563955639656397563985639956400564015640256403564045640556406564075640856409564105641156412564135641456415564165641756418564195642056421564225642356424564255642656427564285642956430564315643256433564345643556436564375643856439564405644156442564435644456445564465644756448564495645056451564525645356454564555645656457564585645956460564615646256463564645646556466564675646856469564705647156472564735647456475564765647756478564795648056481564825648356484564855648656487564885648956490564915649256493564945649556496564975649856499565005650156502565035650456505565065650756508565095651056511565125651356514565155651656517565185651956520565215652256523565245652556526565275652856529565305653156532565335653456535565365653756538565395654056541565425654356544565455654656547565485654956550565515655256553565545655556556565575655856559565605656156562565635656456565565665656756568565695657056571565725657356574565755657656577565785657956580565815658256583565845658556586565875658856589565905659156592565935659456595565965659756598565995660056601566025660356604566055660656607566085660956610566115661256613566145661556616566175661856619566205662156622566235662456625566265662756628566295663056631566325663356634566355663656637566385663956640566415664256643566445664556646566475664856649566505665156652566535665456655566565665756658566595666056661566625666356664566655666656667566685666956670566715667256673566745667556676566775667856679566805668156682566835668456685566865668756688566895669056691566925669356694566955669656697566985669956700567015670256703567045670556706567075670856709567105671156712567135671456715567165671756718567195672056721567225672356724567255672656727567285672956730567315673256733567345673556736567375673856739567405674156742567435674456745567465674756748567495675056751567525675356754567555675656757567585675956760567615676256763567645676556766567675676856769567705677156772567735677456775567765677756778567795678056781567825678356784567855678656787567885678956790567915679256793567945679556796567975679856799568005680156802568035680456805568065680756808568095681056811568125681356814568155681656817568185681956820568215682256823568245682556826568275682856829568305683156832568335683456835568365683756838568395684056841568425684356844568455684656847568485684956850568515685256853568545685556856568575685856859568605686156862568635686456865568665686756868568695687056871568725687356874568755687656877568785687956880568815688256883568845688556886568875688856889568905689156892568935689456895568965689756898568995690056901569025690356904569055690656907569085690956910569115691256913569145691556916569175691856919569205692156922569235692456925569265692756928569295693056931569325693356934569355693656937569385693956940569415694256943569445694556946569475694856949569505695156952569535695456955569565695756958569595696056961569625696356964569655696656967569685696956970569715697256973569745697556976569775697856979569805698156982569835698456985569865698756988569895699056991569925699356994569955699656997569985699957000570015700257003570045700557006570075700857009570105701157012570135701457015570165701757018570195702057021570225702357024570255702657027570285702957030570315703257033570345703557036570375703857039570405704157042570435704457045570465704757048570495705057051570525705357054570555705657057570585705957060570615706257063570645706557066570675706857069570705707157072570735707457075570765707757078570795708057081570825708357084570855708657087570885708957090570915709257093570945709557096570975709857099571005710157102571035710457105571065710757108571095711057111571125711357114571155711657117571185711957120571215712257123571245712557126571275712857129571305713157132571335713457135571365713757138571395714057141571425714357144571455714657147571485714957150571515715257153571545715557156571575715857159571605716157162571635716457165571665716757168571695717057171571725717357174571755717657177571785717957180571815718257183571845718557186571875718857189571905719157192571935719457195571965719757198571995720057201572025720357204572055720657207572085720957210572115721257213572145721557216572175721857219572205722157222572235722457225572265722757228572295723057231572325723357234572355723657237572385723957240572415724257243572445724557246572475724857249572505725157252572535725457255572565725757258572595726057261572625726357264572655726657267572685726957270572715727257273572745727557276572775727857279572805728157282572835728457285572865728757288572895729057291572925729357294572955729657297572985729957300573015730257303573045730557306573075730857309573105731157312573135731457315573165731757318573195732057321573225732357324573255732657327573285732957330573315733257333573345733557336573375733857339573405734157342573435734457345573465734757348573495735057351573525735357354573555735657357573585735957360573615736257363573645736557366573675736857369573705737157372573735737457375573765737757378573795738057381573825738357384573855738657387573885738957390573915739257393573945739557396573975739857399574005740157402574035740457405574065740757408574095741057411574125741357414574155741657417574185741957420574215742257423574245742557426574275742857429574305743157432574335743457435574365743757438574395744057441574425744357444574455744657447574485744957450574515745257453574545745557456574575745857459574605746157462574635746457465574665746757468574695747057471574725747357474574755747657477574785747957480574815748257483574845748557486574875748857489574905749157492574935749457495574965749757498574995750057501575025750357504575055750657507575085750957510575115751257513575145751557516575175751857519575205752157522575235752457525575265752757528575295753057531575325753357534575355753657537575385753957540575415754257543575445754557546575475754857549575505755157552575535755457555575565755757558575595756057561575625756357564575655756657567575685756957570575715757257573575745757557576575775757857579575805758157582575835758457585575865758757588575895759057591575925759357594575955759657597575985759957600576015760257603576045760557606576075760857609576105761157612576135761457615576165761757618576195762057621576225762357624576255762657627576285762957630576315763257633576345763557636576375763857639576405764157642576435764457645576465764757648576495765057651576525765357654576555765657657576585765957660576615766257663576645766557666576675766857669576705767157672576735767457675576765767757678576795768057681576825768357684576855768657687576885768957690576915769257693576945769557696576975769857699577005770157702577035770457705577065770757708577095771057711577125771357714577155771657717577185771957720577215772257723577245772557726577275772857729577305773157732577335773457735577365773757738577395774057741577425774357744577455774657747577485774957750577515775257753577545775557756577575775857759577605776157762577635776457765577665776757768577695777057771577725777357774577755777657777577785777957780577815778257783577845778557786577875778857789577905779157792577935779457795577965779757798577995780057801578025780357804578055780657807578085780957810578115781257813578145781557816578175781857819578205782157822578235782457825578265782757828578295783057831578325783357834578355783657837578385783957840578415784257843578445784557846578475784857849578505785157852578535785457855578565785757858578595786057861578625786357864578655786657867578685786957870578715787257873578745787557876578775787857879578805788157882578835788457885578865788757888578895789057891578925789357894578955789657897578985789957900579015790257903579045790557906579075790857909579105791157912579135791457915579165791757918579195792057921579225792357924579255792657927579285792957930579315793257933579345793557936579375793857939579405794157942579435794457945579465794757948579495795057951579525795357954579555795657957579585795957960579615796257963579645796557966579675796857969579705797157972579735797457975579765797757978579795798057981579825798357984579855798657987579885798957990579915799257993579945799557996579975799857999580005800158002580035800458005580065800758008580095801058011580125801358014580155801658017580185801958020580215802258023580245802558026580275802858029580305803158032580335803458035580365803758038580395804058041580425804358044580455804658047580485804958050580515805258053580545805558056580575805858059580605806158062580635806458065580665806758068580695807058071580725807358074580755807658077580785807958080580815808258083580845808558086580875808858089580905809158092580935809458095580965809758098580995810058101581025810358104581055810658107581085810958110581115811258113581145811558116581175811858119581205812158122581235812458125581265812758128581295813058131581325813358134581355813658137581385813958140581415814258143581445814558146581475814858149581505815158152581535815458155581565815758158581595816058161581625816358164581655816658167581685816958170581715817258173581745817558176581775817858179581805818158182581835818458185581865818758188581895819058191581925819358194581955819658197581985819958200582015820258203582045820558206582075820858209582105821158212582135821458215582165821758218582195822058221582225822358224582255822658227582285822958230582315823258233582345823558236582375823858239582405824158242582435824458245582465824758248582495825058251582525825358254582555825658257582585825958260582615826258263582645826558266582675826858269582705827158272582735827458275582765827758278582795828058281582825828358284582855828658287582885828958290582915829258293582945829558296582975829858299583005830158302583035830458305583065830758308583095831058311583125831358314583155831658317583185831958320583215832258323583245832558326583275832858329583305833158332583335833458335583365833758338583395834058341583425834358344583455834658347583485834958350583515835258353583545835558356583575835858359583605836158362583635836458365583665836758368583695837058371583725837358374583755837658377583785837958380583815838258383583845838558386583875838858389583905839158392583935839458395583965839758398583995840058401584025840358404584055840658407584085840958410584115841258413584145841558416584175841858419584205842158422584235842458425584265842758428584295843058431584325843358434584355843658437584385843958440584415844258443584445844558446584475844858449584505845158452584535845458455584565845758458584595846058461584625846358464584655846658467584685846958470584715847258473584745847558476584775847858479584805848158482584835848458485584865848758488584895849058491584925849358494584955849658497584985849958500585015850258503585045850558506585075850858509585105851158512585135851458515585165851758518585195852058521585225852358524585255852658527585285852958530585315853258533585345853558536585375853858539585405854158542585435854458545585465854758548585495855058551585525855358554585555855658557585585855958560585615856258563585645856558566585675856858569585705857158572585735857458575585765857758578585795858058581585825858358584585855858658587585885858958590585915859258593585945859558596585975859858599586005860158602586035860458605586065860758608586095861058611586125861358614586155861658617586185861958620586215862258623586245862558626586275862858629586305863158632586335863458635586365863758638586395864058641586425864358644586455864658647586485864958650586515865258653586545865558656586575865858659586605866158662586635866458665586665866758668586695867058671586725867358674586755867658677586785867958680586815868258683586845868558686586875868858689586905869158692586935869458695586965869758698586995870058701587025870358704587055870658707587085870958710587115871258713587145871558716587175871858719587205872158722587235872458725587265872758728587295873058731587325873358734587355873658737587385873958740587415874258743587445874558746587475874858749587505875158752587535875458755587565875758758587595876058761587625876358764587655876658767587685876958770587715877258773587745877558776587775877858779587805878158782587835878458785587865878758788587895879058791587925879358794587955879658797587985879958800588015880258803588045880558806588075880858809588105881158812588135881458815588165881758818588195882058821588225882358824588255882658827588285882958830588315883258833588345883558836588375883858839588405884158842588435884458845588465884758848588495885058851588525885358854588555885658857588585885958860588615886258863588645886558866588675886858869588705887158872588735887458875588765887758878588795888058881588825888358884588855888658887588885888958890588915889258893588945889558896588975889858899589005890158902589035890458905589065890758908589095891058911589125891358914589155891658917589185891958920589215892258923589245892558926589275892858929589305893158932589335893458935589365893758938589395894058941589425894358944589455894658947589485894958950589515895258953589545895558956589575895858959589605896158962589635896458965589665896758968589695897058971589725897358974589755897658977589785897958980589815898258983589845898558986589875898858989589905899158992589935899458995589965899758998589995900059001590025900359004590055900659007590085900959010590115901259013590145901559016590175901859019590205902159022590235902459025590265902759028590295903059031590325903359034590355903659037590385903959040590415904259043590445904559046590475904859049590505905159052590535905459055590565905759058590595906059061590625906359064590655906659067590685906959070590715907259073590745907559076590775907859079590805908159082590835908459085590865908759088590895909059091590925909359094590955909659097590985909959100591015910259103591045910559106591075910859109591105911159112591135911459115591165911759118591195912059121591225912359124591255912659127591285912959130591315913259133591345913559136591375913859139591405914159142591435914459145591465914759148591495915059151591525915359154591555915659157591585915959160591615916259163591645916559166591675916859169591705917159172591735917459175591765917759178591795918059181591825918359184591855918659187591885918959190591915919259193591945919559196591975919859199592005920159202592035920459205592065920759208592095921059211592125921359214592155921659217592185921959220592215922259223592245922559226592275922859229592305923159232592335923459235592365923759238592395924059241592425924359244592455924659247592485924959250592515925259253592545925559256592575925859259592605926159262592635926459265592665926759268592695927059271592725927359274592755927659277592785927959280592815928259283592845928559286592875928859289592905929159292592935929459295592965929759298592995930059301593025930359304593055930659307593085930959310593115931259313593145931559316593175931859319593205932159322593235932459325593265932759328593295933059331593325933359334593355933659337593385933959340593415934259343593445934559346593475934859349593505935159352593535935459355593565935759358593595936059361593625936359364593655936659367593685936959370593715937259373593745937559376593775937859379593805938159382593835938459385593865938759388593895939059391593925939359394593955939659397593985939959400594015940259403594045940559406594075940859409594105941159412594135941459415594165941759418594195942059421594225942359424594255942659427594285942959430594315943259433594345943559436594375943859439594405944159442594435944459445594465944759448594495945059451594525945359454594555945659457594585945959460594615946259463594645946559466594675946859469594705947159472594735947459475594765947759478594795948059481594825948359484594855948659487594885948959490594915949259493594945949559496594975949859499595005950159502595035950459505595065950759508595095951059511595125951359514595155951659517595185951959520595215952259523595245952559526595275952859529595305953159532595335953459535595365953759538595395954059541595425954359544595455954659547595485954959550595515955259553595545955559556595575955859559595605956159562595635956459565595665956759568595695957059571595725957359574595755957659577595785957959580595815958259583595845958559586595875958859589595905959159592595935959459595595965959759598595995960059601596025960359604596055960659607596085960959610596115961259613596145961559616596175961859619596205962159622596235962459625596265962759628596295963059631596325963359634596355963659637596385963959640596415964259643596445964559646596475964859649596505965159652596535965459655596565965759658596595966059661596625966359664596655966659667596685966959670596715967259673596745967559676596775967859679596805968159682596835968459685596865968759688596895969059691596925969359694596955969659697596985969959700597015970259703597045970559706597075970859709597105971159712597135971459715597165971759718597195972059721597225972359724597255972659727597285972959730597315973259733597345973559736597375973859739597405974159742597435974459745597465974759748597495975059751597525975359754597555975659757597585975959760597615976259763597645976559766597675976859769597705977159772597735977459775597765977759778597795978059781597825978359784597855978659787597885978959790597915979259793597945979559796597975979859799598005980159802598035980459805598065980759808598095981059811598125981359814598155981659817598185981959820598215982259823598245982559826598275982859829598305983159832598335983459835598365983759838598395984059841598425984359844598455984659847598485984959850598515985259853598545985559856598575985859859598605986159862598635986459865598665986759868598695987059871598725987359874598755987659877598785987959880598815988259883598845988559886598875988859889598905989159892598935989459895598965989759898598995990059901599025990359904599055990659907599085990959910599115991259913599145991559916599175991859919599205992159922599235992459925599265992759928599295993059931599325993359934599355993659937599385993959940599415994259943599445994559946599475994859949599505995159952599535995459955599565995759958599595996059961599625996359964599655996659967599685996959970599715997259973599745997559976599775997859979599805998159982599835998459985599865998759988599895999059991599925999359994599955999659997599985999960000600016000260003600046000560006600076000860009600106001160012600136001460015600166001760018600196002060021600226002360024600256002660027600286002960030600316003260033600346003560036600376003860039600406004160042600436004460045600466004760048600496005060051600526005360054600556005660057600586005960060600616006260063600646006560066600676006860069600706007160072600736007460075600766007760078600796008060081600826008360084600856008660087600886008960090600916009260093600946009560096600976009860099601006010160102601036010460105601066010760108601096011060111601126011360114601156011660117601186011960120601216012260123601246012560126601276012860129601306013160132601336013460135601366013760138601396014060141601426014360144601456014660147601486014960150601516015260153601546015560156601576015860159601606016160162601636016460165601666016760168601696017060171601726017360174601756017660177601786017960180601816018260183601846018560186601876018860189601906019160192601936019460195601966019760198601996020060201602026020360204602056020660207602086020960210602116021260213602146021560216602176021860219602206022160222602236022460225602266022760228602296023060231602326023360234602356023660237602386023960240602416024260243602446024560246602476024860249602506025160252602536025460255602566025760258602596026060261602626026360264602656026660267602686026960270602716027260273602746027560276602776027860279602806028160282602836028460285602866028760288602896029060291602926029360294602956029660297602986029960300603016030260303603046030560306603076030860309603106031160312603136031460315603166031760318603196032060321603226032360324603256032660327603286032960330603316033260333603346033560336603376033860339603406034160342603436034460345603466034760348603496035060351603526035360354603556035660357603586035960360603616036260363603646036560366603676036860369603706037160372603736037460375603766037760378603796038060381603826038360384603856038660387603886038960390603916039260393603946039560396603976039860399604006040160402604036040460405604066040760408604096041060411604126041360414604156041660417604186041960420604216042260423604246042560426604276042860429604306043160432604336043460435604366043760438604396044060441604426044360444604456044660447604486044960450604516045260453604546045560456604576045860459604606046160462604636046460465604666046760468604696047060471604726047360474604756047660477604786047960480604816048260483604846048560486604876048860489604906049160492604936049460495604966049760498604996050060501605026050360504605056050660507605086050960510605116051260513605146051560516605176051860519605206052160522605236052460525605266052760528605296053060531605326053360534605356053660537605386053960540605416054260543605446054560546605476054860549605506055160552605536055460555605566055760558605596056060561605626056360564605656056660567605686056960570605716057260573605746057560576605776057860579605806058160582605836058460585605866058760588605896059060591605926059360594605956059660597605986059960600606016060260603606046060560606606076060860609606106061160612606136061460615606166061760618606196062060621606226062360624606256062660627606286062960630606316063260633606346063560636606376063860639606406064160642606436064460645606466064760648606496065060651606526065360654606556065660657606586065960660606616066260663606646066560666606676066860669606706067160672606736067460675606766067760678606796068060681606826068360684606856068660687606886068960690606916069260693606946069560696606976069860699607006070160702607036070460705607066070760708607096071060711607126071360714607156071660717607186071960720607216072260723607246072560726607276072860729607306073160732607336073460735607366073760738607396074060741607426074360744607456074660747607486074960750607516075260753607546075560756607576075860759607606076160762607636076460765607666076760768607696077060771607726077360774607756077660777607786077960780607816078260783607846078560786607876078860789607906079160792607936079460795607966079760798607996080060801608026080360804608056080660807608086080960810608116081260813608146081560816608176081860819608206082160822608236082460825608266082760828608296083060831608326083360834608356083660837608386083960840608416084260843608446084560846608476084860849608506085160852608536085460855608566085760858608596086060861608626086360864608656086660867608686086960870608716087260873608746087560876608776087860879608806088160882608836088460885608866088760888608896089060891608926089360894608956089660897608986089960900609016090260903609046090560906609076090860909609106091160912609136091460915609166091760918609196092060921609226092360924609256092660927609286092960930609316093260933609346093560936609376093860939609406094160942609436094460945609466094760948609496095060951609526095360954609556095660957609586095960960609616096260963609646096560966609676096860969609706097160972609736097460975609766097760978609796098060981609826098360984609856098660987609886098960990609916099260993609946099560996609976099860999610006100161002610036100461005610066100761008610096101061011610126101361014610156101661017610186101961020610216102261023610246102561026610276102861029610306103161032610336103461035610366103761038610396104061041610426104361044610456104661047610486104961050610516105261053610546105561056610576105861059610606106161062610636106461065610666106761068610696107061071610726107361074610756107661077610786107961080610816108261083610846108561086610876108861089610906109161092610936109461095610966109761098610996110061101611026110361104611056110661107611086110961110611116111261113611146111561116611176111861119611206112161122611236112461125611266112761128611296113061131611326113361134611356113661137611386113961140611416114261143611446114561146611476114861149611506115161152611536115461155611566115761158611596116061161611626116361164611656116661167611686116961170611716117261173611746117561176611776117861179611806118161182611836118461185611866118761188611896119061191611926119361194611956119661197611986119961200612016120261203612046120561206612076120861209612106121161212612136121461215612166121761218612196122061221612226122361224612256122661227612286122961230612316123261233612346123561236612376123861239612406124161242612436124461245612466124761248612496125061251612526125361254612556125661257612586125961260612616126261263612646126561266612676126861269612706127161272612736127461275612766127761278612796128061281612826128361284612856128661287612886128961290612916129261293612946129561296612976129861299613006130161302613036130461305613066130761308613096131061311613126131361314613156131661317613186131961320613216132261323613246132561326613276132861329613306133161332613336133461335613366133761338613396134061341613426134361344613456134661347613486134961350613516135261353613546135561356613576135861359613606136161362613636136461365613666136761368613696137061371613726137361374613756137661377613786137961380613816138261383613846138561386613876138861389613906139161392613936139461395613966139761398613996140061401614026140361404614056140661407614086140961410614116141261413614146141561416614176141861419614206142161422614236142461425614266142761428614296143061431614326143361434614356143661437614386143961440614416144261443614446144561446614476144861449614506145161452614536145461455614566145761458614596146061461614626146361464614656146661467614686146961470614716147261473614746147561476614776147861479614806148161482614836148461485614866148761488614896149061491614926149361494614956149661497614986149961500615016150261503615046150561506615076150861509615106151161512615136151461515615166151761518615196152061521615226152361524615256152661527615286152961530615316153261533615346153561536615376153861539615406154161542615436154461545615466154761548615496155061551615526155361554615556155661557615586155961560615616156261563615646156561566615676156861569615706157161572615736157461575615766157761578615796158061581615826158361584615856158661587615886158961590615916159261593615946159561596615976159861599616006160161602616036160461605616066160761608616096161061611616126161361614616156161661617616186161961620616216162261623616246162561626616276162861629616306163161632616336163461635616366163761638616396164061641616426164361644616456164661647616486164961650616516165261653616546165561656616576165861659616606166161662616636166461665616666166761668616696167061671616726167361674616756167661677616786167961680616816168261683616846168561686616876168861689616906169161692616936169461695616966169761698616996170061701617026170361704617056170661707617086170961710617116171261713617146171561716617176171861719617206172161722617236172461725617266172761728617296173061731617326173361734617356173661737617386173961740617416174261743617446174561746617476174861749617506175161752617536175461755617566175761758617596176061761617626176361764617656176661767617686176961770617716177261773617746177561776617776177861779617806178161782617836178461785617866178761788617896179061791617926179361794617956179661797617986179961800618016180261803618046180561806618076180861809618106181161812618136181461815618166181761818618196182061821618226182361824618256182661827618286182961830618316183261833618346183561836618376183861839618406184161842618436184461845618466184761848618496185061851618526185361854618556185661857618586185961860618616186261863618646186561866618676186861869618706187161872618736187461875618766187761878618796188061881618826188361884618856188661887618886188961890618916189261893618946189561896618976189861899619006190161902619036190461905619066190761908619096191061911619126191361914619156191661917619186191961920619216192261923619246192561926619276192861929619306193161932619336193461935619366193761938619396194061941619426194361944619456194661947619486194961950619516195261953619546195561956619576195861959619606196161962619636196461965619666196761968619696197061971619726197361974619756197661977619786197961980619816198261983619846198561986619876198861989619906199161992619936199461995619966199761998619996200062001620026200362004620056200662007620086200962010620116201262013620146201562016620176201862019620206202162022620236202462025620266202762028620296203062031620326203362034620356203662037620386203962040620416204262043620446204562046620476204862049620506205162052620536205462055620566205762058620596206062061620626206362064620656206662067620686206962070620716207262073620746207562076620776207862079620806208162082620836208462085620866208762088620896209062091620926209362094620956209662097620986209962100621016210262103621046210562106621076210862109621106211162112621136211462115621166211762118621196212062121621226212362124621256212662127621286212962130621316213262133621346213562136621376213862139621406214162142621436214462145621466214762148621496215062151621526215362154621556215662157621586215962160621616216262163621646216562166621676216862169621706217162172621736217462175621766217762178621796218062181621826218362184621856218662187621886218962190621916219262193621946219562196621976219862199622006220162202622036220462205622066220762208622096221062211622126221362214622156221662217622186221962220622216222262223622246222562226622276222862229622306223162232622336223462235622366223762238622396224062241622426224362244622456224662247622486224962250622516225262253622546225562256622576225862259622606226162262622636226462265622666226762268622696227062271622726227362274622756227662277622786227962280622816228262283622846228562286622876228862289622906229162292622936229462295622966229762298622996230062301623026230362304623056230662307623086230962310623116231262313623146231562316623176231862319623206232162322623236232462325623266232762328623296233062331623326233362334623356233662337623386233962340623416234262343623446234562346623476234862349623506235162352623536235462355623566235762358623596236062361623626236362364623656236662367623686236962370623716237262373623746237562376623776237862379623806238162382623836238462385623866238762388623896239062391623926239362394623956239662397623986239962400624016240262403624046240562406624076240862409624106241162412624136241462415624166241762418624196242062421624226242362424624256242662427624286242962430624316243262433624346243562436624376243862439624406244162442624436244462445624466244762448624496245062451624526245362454624556245662457624586245962460624616246262463624646246562466624676246862469624706247162472624736247462475624766247762478624796248062481624826248362484624856248662487624886248962490624916249262493624946249562496624976249862499625006250162502625036250462505625066250762508625096251062511625126251362514625156251662517625186251962520625216252262523625246252562526625276252862529625306253162532625336253462535625366253762538625396254062541625426254362544625456254662547625486254962550625516255262553625546255562556625576255862559625606256162562625636256462565625666256762568625696257062571625726257362574625756257662577625786257962580625816258262583625846258562586625876258862589625906259162592625936259462595625966259762598625996260062601626026260362604626056260662607626086260962610626116261262613626146261562616626176261862619626206262162622626236262462625626266262762628626296263062631626326263362634626356263662637626386263962640626416264262643626446264562646626476264862649626506265162652626536265462655626566265762658626596266062661626626266362664626656266662667626686266962670626716267262673626746267562676626776267862679626806268162682626836268462685626866268762688626896269062691626926269362694626956269662697626986269962700627016270262703627046270562706627076270862709627106271162712627136271462715627166271762718627196272062721627226272362724627256272662727627286272962730627316273262733627346273562736627376273862739627406274162742627436274462745627466274762748627496275062751627526275362754627556275662757627586275962760627616276262763627646276562766627676276862769627706277162772627736277462775627766277762778627796278062781627826278362784627856278662787627886278962790627916279262793627946279562796627976279862799628006280162802628036280462805628066280762808628096281062811628126281362814628156281662817628186281962820628216282262823628246282562826628276282862829628306283162832628336283462835628366283762838628396284062841628426284362844628456284662847628486284962850628516285262853628546285562856628576285862859628606286162862628636286462865628666286762868628696287062871628726287362874628756287662877628786287962880628816288262883628846288562886628876288862889628906289162892628936289462895628966289762898628996290062901629026290362904629056290662907629086290962910629116291262913629146291562916629176291862919629206292162922629236292462925629266292762928629296293062931629326293362934629356293662937629386293962940629416294262943629446294562946629476294862949629506295162952629536295462955629566295762958629596296062961629626296362964629656296662967629686296962970629716297262973629746297562976629776297862979629806298162982629836298462985629866298762988629896299062991629926299362994629956299662997629986299963000630016300263003630046300563006630076300863009630106301163012630136301463015630166301763018630196302063021630226302363024630256302663027630286302963030630316303263033630346303563036630376303863039630406304163042630436304463045630466304763048630496305063051630526305363054630556305663057630586305963060630616306263063630646306563066630676306863069630706307163072630736307463075630766307763078630796308063081630826308363084630856308663087630886308963090630916309263093630946309563096630976309863099631006310163102631036310463105631066310763108631096311063111631126311363114631156311663117631186311963120631216312263123631246312563126631276312863129631306313163132631336313463135631366313763138631396314063141631426314363144631456314663147631486314963150631516315263153631546315563156631576315863159631606316163162631636316463165631666316763168631696317063171631726317363174631756317663177631786317963180631816318263183631846318563186631876318863189631906319163192631936319463195631966319763198631996320063201632026320363204632056320663207632086320963210632116321263213632146321563216632176321863219632206322163222632236322463225632266322763228632296323063231632326323363234632356323663237632386323963240632416324263243632446324563246632476324863249632506325163252632536325463255632566325763258632596326063261632626326363264632656326663267632686326963270632716327263273632746327563276632776327863279632806328163282632836328463285632866328763288632896329063291632926329363294632956329663297632986329963300633016330263303633046330563306633076330863309633106331163312633136331463315633166331763318633196332063321633226332363324633256332663327633286332963330633316333263333633346333563336633376333863339633406334163342633436334463345633466334763348633496335063351633526335363354633556335663357633586335963360633616336263363633646336563366633676336863369633706337163372633736337463375633766337763378633796338063381633826338363384633856338663387633886338963390633916339263393633946339563396633976339863399634006340163402634036340463405634066340763408634096341063411634126341363414634156341663417634186341963420634216342263423634246342563426634276342863429634306343163432634336343463435634366343763438634396344063441634426344363444634456344663447634486344963450634516345263453634546345563456634576345863459634606346163462634636346463465634666346763468634696347063471634726347363474634756347663477634786347963480634816348263483634846348563486634876348863489634906349163492634936349463495634966349763498634996350063501635026350363504635056350663507635086350963510635116351263513635146351563516635176351863519635206352163522635236352463525635266352763528635296353063531635326353363534635356353663537635386353963540635416354263543635446354563546635476354863549635506355163552635536355463555635566355763558635596356063561635626356363564635656356663567635686356963570635716357263573635746357563576635776357863579635806358163582635836358463585635866358763588635896359063591635926359363594635956359663597635986359963600636016360263603636046360563606636076360863609636106361163612636136361463615636166361763618636196362063621636226362363624636256362663627636286362963630636316363263633636346363563636636376363863639636406364163642636436364463645636466364763648636496365063651636526365363654636556365663657636586365963660636616366263663636646366563666636676366863669636706367163672636736367463675636766367763678636796368063681636826368363684636856368663687636886368963690636916369263693636946369563696636976369863699637006370163702637036370463705637066370763708637096371063711637126371363714637156371663717637186371963720637216372263723637246372563726637276372863729637306373163732637336373463735637366373763738637396374063741637426374363744637456374663747637486374963750637516375263753637546375563756637576375863759637606376163762637636376463765637666376763768637696377063771637726377363774637756377663777637786377963780637816378263783637846378563786637876378863789637906379163792637936379463795637966379763798637996380063801638026380363804638056380663807638086380963810638116381263813638146381563816638176381863819638206382163822638236382463825638266382763828638296383063831638326383363834638356383663837638386383963840638416384263843638446384563846638476384863849638506385163852638536385463855638566385763858638596386063861638626386363864638656386663867638686386963870638716387263873638746387563876638776387863879638806388163882638836388463885638866388763888638896389063891638926389363894638956389663897638986389963900639016390263903639046390563906639076390863909639106391163912639136391463915639166391763918639196392063921639226392363924639256392663927639286392963930639316393263933639346393563936639376393863939639406394163942639436394463945639466394763948639496395063951639526395363954639556395663957639586395963960639616396263963639646396563966639676396863969639706397163972639736397463975639766397763978639796398063981639826398363984639856398663987639886398963990639916399263993639946399563996639976399863999640006400164002640036400464005640066400764008640096401064011640126401364014640156401664017640186401964020640216402264023640246402564026640276402864029640306403164032640336403464035640366403764038640396404064041640426404364044640456404664047640486404964050640516405264053640546405564056640576405864059640606406164062640636406464065640666406764068640696407064071640726407364074640756407664077640786407964080640816408264083640846408564086640876408864089640906409164092640936409464095640966409764098640996410064101641026410364104641056410664107641086410964110641116411264113641146411564116641176411864119641206412164122641236412464125641266412764128641296413064131641326413364134641356413664137641386413964140641416414264143641446414564146641476414864149641506415164152641536415464155641566415764158641596416064161641626416364164641656416664167641686416964170641716417264173641746417564176641776417864179641806418164182641836418464185641866418764188641896419064191641926419364194641956419664197641986419964200642016420264203642046420564206642076420864209642106421164212642136421464215642166421764218642196422064221642226422364224642256422664227642286422964230642316423264233642346423564236642376423864239642406424164242642436424464245642466424764248642496425064251642526425364254642556425664257642586425964260642616426264263642646426564266642676426864269642706427164272642736427464275642766427764278642796428064281642826428364284642856428664287642886428964290642916429264293642946429564296642976429864299643006430164302643036430464305643066430764308643096431064311643126431364314643156431664317643186431964320643216432264323643246432564326643276432864329643306433164332643336433464335643366433764338643396434064341643426434364344643456434664347643486434964350643516435264353643546435564356643576435864359643606436164362643636436464365643666436764368643696437064371643726437364374643756437664377643786437964380643816438264383643846438564386643876438864389643906439164392643936439464395643966439764398643996440064401644026440364404644056440664407644086440964410644116441264413644146441564416644176441864419644206442164422644236442464425644266442764428644296443064431644326443364434644356443664437644386443964440644416444264443644446444564446644476444864449644506445164452644536445464455644566445764458644596446064461644626446364464644656446664467644686446964470644716447264473644746447564476644776447864479644806448164482644836448464485644866448764488644896449064491644926449364494644956449664497644986449964500645016450264503645046450564506645076450864509645106451164512645136451464515645166451764518645196452064521645226452364524645256452664527645286452964530645316453264533645346453564536645376453864539645406454164542645436454464545645466454764548645496455064551645526455364554645556455664557645586455964560645616456264563645646456564566645676456864569645706457164572645736457464575645766457764578645796458064581645826458364584645856458664587645886458964590645916459264593645946459564596645976459864599646006460164602646036460464605646066460764608646096461064611646126461364614646156461664617646186461964620646216462264623646246462564626646276462864629646306463164632646336463464635646366463764638646396464064641646426464364644646456464664647646486464964650646516465264653646546465564656646576465864659646606466164662646636466464665646666466764668646696467064671646726467364674646756467664677646786467964680646816468264683646846468564686646876468864689646906469164692646936469464695646966469764698646996470064701647026470364704647056470664707647086470964710647116471264713647146471564716647176471864719647206472164722647236472464725647266472764728647296473064731647326473364734647356473664737647386473964740647416474264743647446474564746647476474864749647506475164752647536475464755647566475764758647596476064761647626476364764647656476664767647686476964770647716477264773647746477564776647776477864779647806478164782647836478464785647866478764788647896479064791647926479364794647956479664797647986479964800648016480264803648046480564806648076480864809648106481164812648136481464815648166481764818648196482064821648226482364824648256482664827648286482964830648316483264833648346483564836648376483864839648406484164842648436484464845648466484764848648496485064851648526485364854648556485664857648586485964860648616486264863648646486564866648676486864869648706487164872648736487464875648766487764878648796488064881648826488364884648856488664887648886488964890648916489264893648946489564896648976489864899649006490164902649036490464905649066490764908649096491064911649126491364914649156491664917649186491964920649216492264923649246492564926649276492864929649306493164932649336493464935649366493764938649396494064941649426494364944649456494664947649486494964950649516495264953649546495564956649576495864959649606496164962649636496464965649666496764968649696497064971649726497364974649756497664977649786497964980649816498264983649846498564986649876498864989649906499164992649936499464995649966499764998649996500065001650026500365004650056500665007650086500965010650116501265013650146501565016650176501865019650206502165022650236502465025650266502765028650296503065031650326503365034650356503665037650386503965040650416504265043650446504565046650476504865049650506505165052650536505465055650566505765058650596506065061650626506365064650656506665067650686506965070650716507265073650746507565076650776507865079650806508165082650836508465085650866508765088650896509065091650926509365094650956509665097650986509965100651016510265103651046510565106651076510865109651106511165112651136511465115651166511765118651196512065121651226512365124651256512665127651286512965130651316513265133651346513565136651376513865139651406514165142651436514465145651466514765148651496515065151651526515365154651556515665157651586515965160651616516265163651646516565166651676516865169651706517165172651736517465175651766517765178651796518065181651826518365184651856518665187651886518965190651916519265193651946519565196651976519865199652006520165202652036520465205652066520765208652096521065211652126521365214652156521665217652186521965220652216522265223652246522565226652276522865229652306523165232652336523465235652366523765238652396524065241652426524365244652456524665247652486524965250652516525265253652546525565256652576525865259652606526165262652636526465265652666526765268652696527065271652726527365274652756527665277652786527965280652816528265283652846528565286652876528865289652906529165292652936529465295652966529765298652996530065301653026530365304653056530665307653086530965310653116531265313653146531565316653176531865319653206532165322653236532465325653266532765328653296533065331653326533365334653356533665337653386533965340653416534265343653446534565346653476534865349653506535165352653536535465355653566535765358653596536065361653626536365364653656536665367653686536965370653716537265373653746537565376653776537865379653806538165382653836538465385653866538765388653896539065391653926539365394653956539665397653986539965400654016540265403654046540565406654076540865409654106541165412654136541465415654166541765418654196542065421654226542365424654256542665427654286542965430654316543265433654346543565436654376543865439654406544165442654436544465445654466544765448654496545065451654526545365454654556545665457654586545965460654616546265463654646546565466654676546865469654706547165472654736547465475654766547765478654796548065481654826548365484654856548665487654886548965490654916549265493654946549565496654976549865499655006550165502655036550465505655066550765508655096551065511655126551365514655156551665517655186551965520655216552265523655246552565526655276552865529655306553165532655336553465535655366553765538655396554065541655426554365544655456554665547655486554965550655516555265553655546555565556655576555865559655606556165562655636556465565655666556765568655696557065571655726557365574655756557665577655786557965580655816558265583655846558565586655876558865589655906559165592655936559465595655966559765598655996560065601656026560365604656056560665607656086560965610656116561265613656146561565616656176561865619656206562165622656236562465625656266562765628656296563065631656326563365634656356563665637656386563965640656416564265643656446564565646656476564865649656506565165652656536565465655656566565765658656596566065661656626566365664656656566665667656686566965670656716567265673656746567565676656776567865679656806568165682656836568465685656866568765688656896569065691656926569365694656956569665697656986569965700657016570265703657046570565706657076570865709657106571165712657136571465715657166571765718657196572065721657226572365724657256572665727657286572965730657316573265733657346573565736657376573865739657406574165742657436574465745657466574765748657496575065751657526575365754657556575665757657586575965760657616576265763657646576565766657676576865769657706577165772657736577465775657766577765778657796578065781657826578365784657856578665787657886578965790657916579265793657946579565796657976579865799658006580165802658036580465805658066580765808658096581065811658126581365814658156581665817658186581965820658216582265823658246582565826658276582865829658306583165832658336583465835658366583765838658396584065841658426584365844658456584665847658486584965850658516585265853658546585565856658576585865859658606586165862658636586465865658666586765868658696587065871658726587365874658756587665877658786587965880658816588265883658846588565886658876588865889658906589165892658936589465895658966589765898658996590065901659026590365904659056590665907659086590965910659116591265913659146591565916659176591865919659206592165922659236592465925659266592765928659296593065931659326593365934659356593665937659386593965940659416594265943659446594565946659476594865949659506595165952659536595465955659566595765958659596596065961659626596365964659656596665967659686596965970659716597265973659746597565976659776597865979659806598165982659836598465985659866598765988659896599065991659926599365994659956599665997659986599966000660016600266003660046600566006660076600866009660106601166012660136601466015660166601766018660196602066021660226602366024660256602666027660286602966030660316603266033660346603566036660376603866039660406604166042660436604466045660466604766048660496605066051660526605366054660556605666057660586605966060660616606266063660646606566066660676606866069660706607166072660736607466075660766607766078660796608066081660826608366084660856608666087660886608966090660916609266093660946609566096660976609866099661006610166102661036610466105661066610766108661096611066111661126611366114661156611666117661186611966120661216612266123661246612566126661276612866129661306613166132661336613466135661366613766138661396614066141661426614366144661456614666147661486614966150661516615266153661546615566156661576615866159661606616166162661636616466165661666616766168661696617066171661726617366174661756617666177661786617966180661816618266183661846618566186661876618866189661906619166192661936619466195661966619766198661996620066201662026620366204662056620666207662086620966210662116621266213662146621566216662176621866219662206622166222662236622466225662266622766228662296623066231662326623366234662356623666237662386623966240662416624266243662446624566246662476624866249662506625166252662536625466255662566625766258662596626066261662626626366264662656626666267662686626966270662716627266273662746627566276662776627866279662806628166282662836628466285662866628766288662896629066291662926629366294662956629666297662986629966300663016630266303663046630566306663076630866309663106631166312663136631466315663166631766318663196632066321663226632366324663256632666327663286632966330663316633266333663346633566336663376633866339663406634166342663436634466345663466634766348663496635066351663526635366354663556635666357663586635966360663616636266363663646636566366663676636866369663706637166372663736637466375663766637766378663796638066381663826638366384663856638666387663886638966390663916639266393663946639566396663976639866399664006640166402664036640466405664066640766408664096641066411664126641366414664156641666417664186641966420664216642266423664246642566426664276642866429664306643166432664336643466435664366643766438664396644066441664426644366444664456644666447664486644966450664516645266453664546645566456664576645866459664606646166462664636646466465664666646766468664696647066471664726647366474664756647666477664786647966480664816648266483664846648566486664876648866489664906649166492664936649466495664966649766498664996650066501665026650366504665056650666507665086650966510665116651266513665146651566516665176651866519665206652166522665236652466525665266652766528665296653066531665326653366534665356653666537665386653966540665416654266543665446654566546665476654866549665506655166552665536655466555665566655766558665596656066561665626656366564665656656666567665686656966570665716657266573665746657566576665776657866579665806658166582665836658466585665866658766588665896659066591665926659366594665956659666597665986659966600666016660266603666046660566606666076660866609666106661166612666136661466615666166661766618666196662066621666226662366624666256662666627666286662966630666316663266633666346663566636666376663866639666406664166642666436664466645666466664766648666496665066651666526665366654666556665666657666586665966660666616666266663666646666566666666676666866669666706667166672666736667466675666766667766678666796668066681666826668366684666856668666687666886668966690666916669266693666946669566696666976669866699667006670166702667036670466705667066670766708667096671066711667126671366714667156671666717667186671966720667216672266723667246672566726667276672866729667306673166732667336673466735667366673766738667396674066741667426674366744667456674666747667486674966750667516675266753667546675566756667576675866759667606676166762667636676466765667666676766768667696677066771667726677366774667756677666777667786677966780667816678266783667846678566786667876678866789667906679166792667936679466795667966679766798667996680066801668026680366804668056680666807668086680966810668116681266813668146681566816668176681866819668206682166822668236682466825668266682766828668296683066831668326683366834668356683666837668386683966840668416684266843668446684566846668476684866849668506685166852668536685466855668566685766858668596686066861668626686366864668656686666867668686686966870668716687266873668746687566876668776687866879668806688166882668836688466885668866688766888668896689066891668926689366894668956689666897668986689966900669016690266903669046690566906669076690866909669106691166912669136691466915669166691766918669196692066921669226692366924669256692666927669286692966930669316693266933669346693566936669376693866939669406694166942669436694466945669466694766948669496695066951669526695366954669556695666957669586695966960669616696266963669646696566966669676696866969669706697166972669736697466975669766697766978669796698066981669826698366984669856698666987669886698966990669916699266993669946699566996669976699866999670006700167002670036700467005670066700767008670096701067011670126701367014670156701667017670186701967020670216702267023670246702567026670276702867029670306703167032670336703467035670366703767038670396704067041670426704367044670456704667047670486704967050670516705267053670546705567056670576705867059670606706167062670636706467065670666706767068670696707067071670726707367074670756707667077670786707967080670816708267083670846708567086670876708867089670906709167092670936709467095670966709767098670996710067101671026710367104671056710667107671086710967110671116711267113671146711567116671176711867119671206712167122671236712467125671266712767128671296713067131671326713367134671356713667137671386713967140671416714267143671446714567146671476714867149671506715167152671536715467155671566715767158671596716067161671626716367164671656716667167671686716967170671716717267173671746717567176671776717867179671806718167182671836718467185671866718767188671896719067191671926719367194671956719667197671986719967200672016720267203672046720567206672076720867209672106721167212672136721467215672166721767218672196722067221672226722367224672256722667227672286722967230672316723267233672346723567236672376723867239672406724167242672436724467245672466724767248672496725067251672526725367254672556725667257672586725967260672616726267263672646726567266672676726867269672706727167272672736727467275672766727767278672796728067281672826728367284672856728667287672886728967290672916729267293672946729567296672976729867299673006730167302673036730467305673066730767308673096731067311673126731367314673156731667317673186731967320673216732267323673246732567326673276732867329673306733167332673336733467335673366733767338673396734067341673426734367344673456734667347673486734967350673516735267353673546735567356673576735867359673606736167362673636736467365673666736767368673696737067371673726737367374673756737667377673786737967380673816738267383673846738567386673876738867389673906739167392673936739467395673966739767398673996740067401674026740367404674056740667407674086740967410674116741267413674146741567416674176741867419674206742167422674236742467425674266742767428674296743067431674326743367434674356743667437674386743967440674416744267443674446744567446674476744867449674506745167452674536745467455674566745767458674596746067461674626746367464674656746667467674686746967470674716747267473674746747567476674776747867479674806748167482674836748467485674866748767488674896749067491674926749367494674956749667497674986749967500675016750267503675046750567506675076750867509675106751167512675136751467515675166751767518675196752067521675226752367524675256752667527675286752967530675316753267533675346753567536675376753867539675406754167542675436754467545675466754767548675496755067551675526755367554675556755667557675586755967560675616756267563675646756567566675676756867569675706757167572675736757467575675766757767578675796758067581675826758367584675856758667587675886758967590675916759267593675946759567596675976759867599676006760167602676036760467605676066760767608676096761067611676126761367614676156761667617676186761967620676216762267623676246762567626676276762867629676306763167632676336763467635676366763767638676396764067641676426764367644676456764667647676486764967650676516765267653676546765567656676576765867659676606766167662676636766467665676666766767668676696767067671676726767367674676756767667677676786767967680676816768267683676846768567686676876768867689676906769167692676936769467695676966769767698676996770067701677026770367704677056770667707677086770967710677116771267713677146771567716677176771867719677206772167722677236772467725677266772767728677296773067731677326773367734677356773667737677386773967740677416774267743677446774567746677476774867749677506775167752677536775467755677566775767758677596776067761677626776367764677656776667767677686776967770677716777267773677746777567776677776777867779677806778167782677836778467785677866778767788677896779067791677926779367794677956779667797677986779967800678016780267803678046780567806678076780867809678106781167812678136781467815678166781767818678196782067821678226782367824678256782667827678286782967830678316783267833678346783567836678376783867839678406784167842678436784467845678466784767848678496785067851678526785367854678556785667857678586785967860678616786267863678646786567866678676786867869678706787167872678736787467875678766787767878678796788067881678826788367884678856788667887678886788967890678916789267893678946789567896678976789867899679006790167902679036790467905679066790767908679096791067911679126791367914679156791667917679186791967920679216792267923679246792567926679276792867929679306793167932679336793467935679366793767938679396794067941679426794367944679456794667947679486794967950679516795267953679546795567956679576795867959679606796167962679636796467965679666796767968679696797067971679726797367974679756797667977679786797967980679816798267983679846798567986679876798867989679906799167992679936799467995679966799767998679996800068001680026800368004680056800668007680086800968010680116801268013680146801568016680176801868019680206802168022680236802468025680266802768028680296803068031680326803368034680356803668037680386803968040680416804268043680446804568046680476804868049680506805168052680536805468055680566805768058680596806068061680626806368064680656806668067680686806968070680716807268073680746807568076680776807868079680806808168082680836808468085680866808768088680896809068091680926809368094680956809668097680986809968100681016810268103681046810568106681076810868109681106811168112681136811468115681166811768118681196812068121681226812368124681256812668127681286812968130681316813268133681346813568136681376813868139681406814168142681436814468145681466814768148681496815068151681526815368154681556815668157681586815968160681616816268163681646816568166681676816868169681706817168172681736817468175681766817768178681796818068181681826818368184681856818668187681886818968190681916819268193681946819568196681976819868199682006820168202682036820468205682066820768208682096821068211682126821368214682156821668217682186821968220682216822268223682246822568226682276822868229682306823168232682336823468235682366823768238682396824068241682426824368244682456824668247682486824968250682516825268253682546825568256682576825868259682606826168262682636826468265682666826768268682696827068271682726827368274682756827668277682786827968280682816828268283682846828568286682876828868289682906829168292682936829468295682966829768298682996830068301683026830368304683056830668307683086830968310683116831268313683146831568316683176831868319683206832168322683236832468325683266832768328683296833068331683326833368334683356833668337683386833968340683416834268343683446834568346683476834868349683506835168352683536835468355683566835768358683596836068361683626836368364683656836668367683686836968370683716837268373683746837568376683776837868379683806838168382683836838468385683866838768388683896839068391683926839368394683956839668397683986839968400684016840268403684046840568406684076840868409684106841168412684136841468415684166841768418684196842068421684226842368424684256842668427684286842968430684316843268433684346843568436684376843868439684406844168442684436844468445684466844768448684496845068451684526845368454684556845668457684586845968460684616846268463684646846568466684676846868469684706847168472684736847468475684766847768478684796848068481684826848368484684856848668487684886848968490684916849268493684946849568496684976849868499685006850168502685036850468505685066850768508685096851068511685126851368514685156851668517685186851968520685216852268523685246852568526685276852868529685306853168532685336853468535685366853768538685396854068541685426854368544685456854668547685486854968550685516855268553685546855568556685576855868559685606856168562685636856468565685666856768568685696857068571685726857368574685756857668577685786857968580685816858268583685846858568586685876858868589685906859168592685936859468595685966859768598685996860068601686026860368604686056860668607686086860968610686116861268613686146861568616686176861868619686206862168622686236862468625686266862768628686296863068631686326863368634686356863668637686386863968640686416864268643686446864568646686476864868649686506865168652686536865468655686566865768658686596866068661686626866368664686656866668667686686866968670686716867268673686746867568676686776867868679686806868168682686836868468685686866868768688686896869068691686926869368694686956869668697686986869968700687016870268703687046870568706687076870868709687106871168712687136871468715687166871768718687196872068721687226872368724687256872668727687286872968730687316873268733687346873568736687376873868739687406874168742687436874468745687466874768748687496875068751687526875368754687556875668757687586875968760687616876268763687646876568766687676876868769687706877168772687736877468775687766877768778687796878068781687826878368784687856878668787687886878968790687916879268793687946879568796687976879868799688006880168802688036880468805688066880768808688096881068811688126881368814688156881668817688186881968820688216882268823688246882568826688276882868829688306883168832688336883468835688366883768838688396884068841688426884368844688456884668847688486884968850688516885268853688546885568856688576885868859688606886168862688636886468865688666886768868688696887068871688726887368874688756887668877688786887968880688816888268883688846888568886688876888868889688906889168892688936889468895688966889768898688996890068901689026890368904689056890668907689086890968910689116891268913689146891568916689176891868919689206892168922689236892468925689266892768928689296893068931689326893368934689356893668937689386893968940689416894268943689446894568946689476894868949689506895168952689536895468955689566895768958689596896068961689626896368964689656896668967689686896968970689716897268973689746897568976689776897868979689806898168982689836898468985689866898768988689896899068991689926899368994689956899668997689986899969000690016900269003690046900569006690076900869009690106901169012690136901469015690166901769018690196902069021690226902369024690256902669027690286902969030690316903269033690346903569036690376903869039690406904169042690436904469045690466904769048690496905069051690526905369054690556905669057690586905969060690616906269063690646906569066690676906869069690706907169072690736907469075690766907769078690796908069081690826908369084690856908669087690886908969090690916909269093690946909569096690976909869099691006910169102691036910469105691066910769108691096911069111691126911369114691156911669117691186911969120691216912269123691246912569126691276912869129691306913169132691336913469135691366913769138691396914069141691426914369144691456914669147691486914969150691516915269153691546915569156691576915869159691606916169162691636916469165691666916769168691696917069171691726917369174691756917669177691786917969180691816918269183691846918569186691876918869189691906919169192691936919469195691966919769198691996920069201692026920369204692056920669207692086920969210692116921269213692146921569216692176921869219692206922169222692236922469225692266922769228692296923069231692326923369234692356923669237692386923969240692416924269243692446924569246692476924869249692506925169252692536925469255692566925769258692596926069261692626926369264692656926669267692686926969270692716927269273692746927569276692776927869279692806928169282692836928469285692866928769288692896929069291692926929369294692956929669297692986929969300693016930269303693046930569306693076930869309693106931169312693136931469315693166931769318693196932069321693226932369324693256932669327693286932969330693316933269333693346933569336693376933869339693406934169342693436934469345693466934769348693496935069351693526935369354693556935669357693586935969360693616936269363693646936569366693676936869369693706937169372693736937469375693766937769378693796938069381693826938369384693856938669387693886938969390693916939269393693946939569396693976939869399694006940169402694036940469405694066940769408694096941069411694126941369414694156941669417694186941969420694216942269423694246942569426694276942869429694306943169432694336943469435694366943769438694396944069441694426944369444694456944669447694486944969450694516945269453694546945569456694576945869459694606946169462694636946469465694666946769468694696947069471694726947369474694756947669477694786947969480694816948269483694846948569486694876948869489694906949169492694936949469495694966949769498694996950069501695026950369504695056950669507695086950969510695116951269513695146951569516695176951869519695206952169522695236952469525695266952769528695296953069531695326953369534695356953669537695386953969540695416954269543695446954569546695476954869549695506955169552695536955469555695566955769558695596956069561695626956369564695656956669567695686956969570695716957269573695746957569576695776957869579695806958169582695836958469585695866958769588695896959069591695926959369594695956959669597695986959969600696016960269603696046960569606696076960869609696106961169612696136961469615696166961769618696196962069621696226962369624696256962669627696286962969630696316963269633696346963569636696376963869639696406964169642696436964469645696466964769648696496965069651696526965369654696556965669657696586965969660696616966269663696646966569666696676966869669696706967169672696736967469675696766967769678696796968069681696826968369684696856968669687696886968969690696916969269693696946969569696696976969869699697006970169702697036970469705697066970769708697096971069711697126971369714697156971669717697186971969720697216972269723697246972569726697276972869729697306973169732697336973469735697366973769738697396974069741697426974369744697456974669747697486974969750697516975269753697546975569756697576975869759697606976169762697636976469765697666976769768697696977069771697726977369774697756977669777697786977969780697816978269783697846978569786697876978869789697906979169792697936979469795697966979769798697996980069801698026980369804698056980669807698086980969810698116981269813698146981569816698176981869819698206982169822698236982469825698266982769828698296983069831698326983369834698356983669837698386983969840698416984269843698446984569846698476984869849698506985169852698536985469855698566985769858698596986069861698626986369864698656986669867698686986969870698716987269873698746987569876698776987869879698806988169882698836988469885698866988769888698896989069891698926989369894698956989669897698986989969900699016990269903699046990569906699076990869909699106991169912699136991469915699166991769918699196992069921699226992369924699256992669927699286992969930699316993269933699346993569936699376993869939699406994169942699436994469945699466994769948699496995069951699526995369954699556995669957699586995969960699616996269963699646996569966699676996869969699706997169972699736997469975699766997769978699796998069981699826998369984699856998669987699886998969990699916999269993699946999569996699976999869999700007000170002700037000470005700067000770008700097001070011700127001370014700157001670017700187001970020700217002270023700247002570026700277002870029700307003170032700337003470035700367003770038700397004070041700427004370044700457004670047700487004970050700517005270053700547005570056700577005870059700607006170062700637006470065700667006770068700697007070071700727007370074700757007670077700787007970080700817008270083700847008570086700877008870089700907009170092700937009470095700967009770098700997010070101701027010370104701057010670107701087010970110701117011270113701147011570116701177011870119701207012170122701237012470125701267012770128701297013070131701327013370134701357013670137701387013970140701417014270143701447014570146701477014870149701507015170152701537015470155701567015770158701597016070161701627016370164701657016670167701687016970170701717017270173701747017570176701777017870179701807018170182701837018470185701867018770188701897019070191701927019370194701957019670197701987019970200702017020270203702047020570206702077020870209702107021170212702137021470215702167021770218702197022070221702227022370224702257022670227702287022970230702317023270233702347023570236702377023870239702407024170242702437024470245702467024770248702497025070251702527025370254702557025670257702587025970260702617026270263702647026570266702677026870269702707027170272702737027470275702767027770278702797028070281702827028370284702857028670287702887028970290702917029270293702947029570296702977029870299703007030170302703037030470305703067030770308703097031070311703127031370314703157031670317703187031970320703217032270323703247032570326703277032870329703307033170332703337033470335703367033770338703397034070341703427034370344703457034670347703487034970350703517035270353703547035570356703577035870359703607036170362703637036470365703667036770368703697037070371703727037370374703757037670377703787037970380703817038270383703847038570386703877038870389703907039170392703937039470395703967039770398703997040070401704027040370404704057040670407704087040970410704117041270413704147041570416704177041870419704207042170422704237042470425704267042770428704297043070431704327043370434704357043670437704387043970440704417044270443704447044570446704477044870449704507045170452704537045470455704567045770458704597046070461704627046370464704657046670467704687046970470704717047270473704747047570476704777047870479704807048170482704837048470485704867048770488704897049070491704927049370494704957049670497704987049970500705017050270503705047050570506705077050870509705107051170512705137051470515705167051770518705197052070521705227052370524705257052670527705287052970530705317053270533705347053570536705377053870539705407054170542705437054470545705467054770548705497055070551705527055370554705557055670557705587055970560705617056270563705647056570566705677056870569705707057170572705737057470575705767057770578705797058070581705827058370584705857058670587705887058970590705917059270593705947059570596705977059870599706007060170602706037060470605706067060770608706097061070611706127061370614706157061670617706187061970620706217062270623706247062570626706277062870629706307063170632706337063470635706367063770638706397064070641706427064370644706457064670647706487064970650706517065270653706547065570656706577065870659706607066170662706637066470665706667066770668706697067070671706727067370674706757067670677706787067970680706817068270683706847068570686706877068870689706907069170692706937069470695706967069770698706997070070701707027070370704707057070670707707087070970710707117071270713707147071570716707177071870719707207072170722707237072470725707267072770728707297073070731707327073370734707357073670737707387073970740707417074270743707447074570746707477074870749707507075170752707537075470755707567075770758707597076070761707627076370764707657076670767707687076970770707717077270773707747077570776707777077870779707807078170782707837078470785707867078770788707897079070791707927079370794707957079670797707987079970800708017080270803708047080570806708077080870809708107081170812708137081470815708167081770818708197082070821708227082370824708257082670827708287082970830708317083270833708347083570836708377083870839708407084170842708437084470845708467084770848708497085070851708527085370854708557085670857708587085970860708617086270863708647086570866708677086870869708707087170872708737087470875708767087770878708797088070881708827088370884708857088670887708887088970890708917089270893708947089570896708977089870899709007090170902709037090470905709067090770908709097091070911709127091370914709157091670917709187091970920709217092270923709247092570926709277092870929709307093170932709337093470935709367093770938709397094070941709427094370944709457094670947709487094970950709517095270953709547095570956709577095870959709607096170962709637096470965709667096770968709697097070971709727097370974709757097670977709787097970980709817098270983709847098570986709877098870989709907099170992709937099470995709967099770998709997100071001710027100371004710057100671007710087100971010710117101271013710147101571016710177101871019710207102171022710237102471025710267102771028710297103071031710327103371034710357103671037710387103971040710417104271043710447104571046710477104871049710507105171052710537105471055710567105771058710597106071061710627106371064710657106671067710687106971070710717107271073710747107571076710777107871079710807108171082710837108471085710867108771088710897109071091710927109371094710957109671097710987109971100711017110271103711047110571106711077110871109711107111171112711137111471115711167111771118711197112071121711227112371124711257112671127711287112971130711317113271133711347113571136711377113871139711407114171142711437114471145711467114771148711497115071151711527115371154711557115671157711587115971160711617116271163711647116571166711677116871169711707117171172711737117471175711767117771178711797118071181711827118371184711857118671187711887118971190711917119271193711947119571196711977119871199712007120171202712037120471205712067120771208712097121071211712127121371214712157121671217712187121971220712217122271223712247122571226712277122871229712307123171232712337123471235712367123771238712397124071241712427124371244712457124671247712487124971250712517125271253712547125571256712577125871259712607126171262712637126471265712667126771268712697127071271712727127371274712757127671277712787127971280712817128271283712847128571286712877128871289712907129171292712937129471295712967129771298712997130071301713027130371304713057130671307713087130971310713117131271313713147131571316713177131871319713207132171322713237132471325713267132771328713297133071331713327133371334713357133671337713387133971340713417134271343713447134571346713477134871349713507135171352713537135471355713567135771358713597136071361713627136371364713657136671367713687136971370713717137271373713747137571376713777137871379713807138171382713837138471385713867138771388713897139071391713927139371394713957139671397713987139971400714017140271403714047140571406714077140871409714107141171412714137141471415714167141771418714197142071421714227142371424714257142671427714287142971430714317143271433714347143571436714377143871439714407144171442714437144471445714467144771448714497145071451714527145371454714557145671457714587145971460714617146271463714647146571466714677146871469714707147171472714737147471475714767147771478714797148071481714827148371484714857148671487714887148971490714917149271493714947149571496714977149871499715007150171502715037150471505715067150771508715097151071511715127151371514715157151671517715187151971520715217152271523715247152571526715277152871529715307153171532715337153471535715367153771538715397154071541715427154371544715457154671547715487154971550715517155271553715547155571556715577155871559715607156171562715637156471565715667156771568715697157071571715727157371574715757157671577715787157971580715817158271583715847158571586715877158871589715907159171592715937159471595715967159771598715997160071601716027160371604716057160671607716087160971610716117161271613716147161571616716177161871619716207162171622716237162471625716267162771628716297163071631716327163371634716357163671637716387163971640716417164271643716447164571646716477164871649716507165171652716537165471655716567165771658716597166071661716627166371664716657166671667716687166971670716717167271673716747167571676716777167871679716807168171682716837168471685716867168771688716897169071691716927169371694716957169671697716987169971700717017170271703717047170571706717077170871709717107171171712717137171471715717167171771718717197172071721717227172371724717257172671727717287172971730717317173271733717347173571736717377173871739717407174171742717437174471745717467174771748717497175071751717527175371754717557175671757717587175971760717617176271763717647176571766717677176871769717707177171772717737177471775717767177771778717797178071781717827178371784717857178671787717887178971790717917179271793717947179571796717977179871799718007180171802718037180471805718067180771808718097181071811718127181371814718157181671817718187181971820718217182271823718247182571826718277182871829718307183171832718337183471835718367183771838718397184071841718427184371844718457184671847718487184971850718517185271853718547185571856718577185871859718607186171862718637186471865718667186771868718697187071871718727187371874718757187671877718787187971880718817188271883718847188571886718877188871889718907189171892718937189471895718967189771898718997190071901719027190371904719057190671907719087190971910719117191271913719147191571916719177191871919719207192171922719237192471925719267192771928719297193071931719327193371934719357193671937719387193971940719417194271943719447194571946719477194871949719507195171952719537195471955719567195771958719597196071961719627196371964719657196671967719687196971970719717197271973719747197571976719777197871979719807198171982719837198471985719867198771988719897199071991719927199371994719957199671997719987199972000720017200272003720047200572006720077200872009720107201172012720137201472015720167201772018720197202072021720227202372024720257202672027720287202972030720317203272033720347203572036720377203872039720407204172042720437204472045720467204772048720497205072051720527205372054720557205672057720587205972060720617206272063720647206572066720677206872069720707207172072720737207472075720767207772078720797208072081720827208372084720857208672087720887208972090720917209272093720947209572096720977209872099721007210172102721037210472105721067210772108721097211072111721127211372114721157211672117721187211972120721217212272123721247212572126721277212872129721307213172132721337213472135721367213772138721397214072141721427214372144721457214672147721487214972150721517215272153721547215572156721577215872159721607216172162721637216472165721667216772168721697217072171721727217372174721757217672177721787217972180721817218272183721847218572186721877218872189721907219172192721937219472195721967219772198721997220072201722027220372204722057220672207722087220972210722117221272213722147221572216722177221872219722207222172222722237222472225722267222772228722297223072231722327223372234722357223672237722387223972240722417224272243722447224572246722477224872249722507225172252722537225472255722567225772258722597226072261722627226372264722657226672267722687226972270722717227272273722747227572276722777227872279722807228172282722837228472285722867228772288722897229072291722927229372294722957229672297722987229972300723017230272303723047230572306723077230872309723107231172312723137231472315723167231772318723197232072321723227232372324723257232672327723287232972330723317233272333723347233572336723377233872339723407234172342723437234472345723467234772348723497235072351723527235372354723557235672357723587235972360723617236272363723647236572366723677236872369723707237172372723737237472375723767237772378723797238072381723827238372384723857238672387723887238972390723917239272393723947239572396723977239872399724007240172402724037240472405724067240772408724097241072411724127241372414724157241672417724187241972420724217242272423724247242572426724277242872429724307243172432724337243472435724367243772438724397244072441724427244372444724457244672447724487244972450724517245272453724547245572456724577245872459724607246172462724637246472465724667246772468724697247072471724727247372474724757247672477724787247972480724817248272483724847248572486724877248872489724907249172492724937249472495724967249772498724997250072501725027250372504725057250672507725087250972510725117251272513725147251572516725177251872519725207252172522725237252472525725267252772528725297253072531725327253372534725357253672537725387253972540725417254272543725447254572546725477254872549725507255172552725537255472555725567255772558725597256072561725627256372564725657256672567725687256972570725717257272573725747257572576725777257872579725807258172582725837258472585725867258772588725897259072591725927259372594725957259672597725987259972600726017260272603726047260572606726077260872609726107261172612726137261472615726167261772618726197262072621726227262372624726257262672627726287262972630726317263272633726347263572636726377263872639726407264172642726437264472645726467264772648726497265072651726527265372654726557265672657726587265972660726617266272663726647266572666726677266872669726707267172672726737267472675726767267772678726797268072681726827268372684726857268672687726887268972690726917269272693726947269572696726977269872699727007270172702727037270472705727067270772708727097271072711727127271372714727157271672717727187271972720727217272272723727247272572726727277272872729727307273172732727337273472735727367273772738727397274072741727427274372744727457274672747727487274972750727517275272753727547275572756727577275872759727607276172762727637276472765727667276772768727697277072771727727277372774727757277672777727787277972780727817278272783727847278572786727877278872789727907279172792727937279472795727967279772798727997280072801728027280372804728057280672807728087280972810728117281272813728147281572816728177281872819728207282172822728237282472825728267282772828728297283072831728327283372834728357283672837728387283972840728417284272843728447284572846728477284872849728507285172852728537285472855728567285772858728597286072861728627286372864728657286672867728687286972870728717287272873728747287572876728777287872879728807288172882728837288472885728867288772888728897289072891728927289372894728957289672897728987289972900729017290272903729047290572906729077290872909729107291172912729137291472915729167291772918729197292072921729227292372924729257292672927729287292972930729317293272933729347293572936729377293872939729407294172942729437294472945729467294772948729497295072951729527295372954729557295672957729587295972960729617296272963729647296572966729677296872969729707297172972729737297472975729767297772978729797298072981729827298372984729857298672987729887298972990729917299272993729947299572996729977299872999730007300173002730037300473005730067300773008730097301073011730127301373014730157301673017730187301973020730217302273023730247302573026730277302873029730307303173032730337303473035730367303773038730397304073041730427304373044730457304673047730487304973050730517305273053730547305573056730577305873059730607306173062730637306473065730667306773068730697307073071730727307373074730757307673077730787307973080730817308273083730847308573086730877308873089730907309173092730937309473095730967309773098730997310073101731027310373104731057310673107731087310973110731117311273113731147311573116731177311873119731207312173122731237312473125731267312773128731297313073131731327313373134731357313673137731387313973140731417314273143731447314573146731477314873149731507315173152731537315473155731567315773158731597316073161731627316373164731657316673167731687316973170731717317273173731747317573176731777317873179731807318173182731837318473185731867318773188731897319073191731927319373194731957319673197731987319973200732017320273203732047320573206732077320873209732107321173212732137321473215732167321773218732197322073221732227322373224732257322673227732287322973230732317323273233732347323573236732377323873239732407324173242732437324473245732467324773248732497325073251732527325373254732557325673257732587325973260732617326273263732647326573266732677326873269732707327173272732737327473275732767327773278732797328073281732827328373284732857328673287732887328973290732917329273293732947329573296732977329873299733007330173302733037330473305733067330773308733097331073311733127331373314733157331673317733187331973320733217332273323733247332573326733277332873329733307333173332733337333473335733367333773338733397334073341733427334373344733457334673347733487334973350733517335273353733547335573356733577335873359733607336173362733637336473365733667336773368733697337073371733727337373374733757337673377733787337973380733817338273383733847338573386733877338873389733907339173392733937339473395733967339773398733997340073401734027340373404734057340673407734087340973410734117341273413734147341573416734177341873419734207342173422734237342473425734267342773428734297343073431734327343373434734357343673437734387343973440734417344273443734447344573446734477344873449734507345173452734537345473455734567345773458734597346073461734627346373464734657346673467734687346973470734717347273473734747347573476734777347873479734807348173482734837348473485734867348773488734897349073491734927349373494734957349673497734987349973500735017350273503735047350573506735077350873509735107351173512735137351473515735167351773518735197352073521735227352373524735257352673527735287352973530735317353273533735347353573536735377353873539735407354173542735437354473545735467354773548735497355073551735527355373554735557355673557735587355973560735617356273563735647356573566735677356873569735707357173572735737357473575735767357773578735797358073581735827358373584735857358673587735887358973590735917359273593735947359573596735977359873599736007360173602736037360473605736067360773608736097361073611736127361373614736157361673617736187361973620736217362273623736247362573626736277362873629736307363173632736337363473635736367363773638736397364073641736427364373644736457364673647736487364973650736517365273653736547365573656736577365873659736607366173662736637366473665736667366773668736697367073671736727367373674736757367673677736787367973680736817368273683736847368573686736877368873689736907369173692736937369473695736967369773698736997370073701737027370373704737057370673707737087370973710737117371273713737147371573716737177371873719737207372173722737237372473725737267372773728737297373073731737327373373734737357373673737737387373973740737417374273743737447374573746737477374873749737507375173752737537375473755737567375773758737597376073761737627376373764737657376673767737687376973770737717377273773737747377573776737777377873779737807378173782737837378473785737867378773788737897379073791737927379373794737957379673797737987379973800738017380273803738047380573806738077380873809738107381173812738137381473815738167381773818738197382073821738227382373824738257382673827738287382973830738317383273833738347383573836738377383873839738407384173842738437384473845738467384773848738497385073851738527385373854738557385673857738587385973860738617386273863738647386573866738677386873869738707387173872738737387473875738767387773878738797388073881738827388373884738857388673887738887388973890738917389273893738947389573896738977389873899739007390173902739037390473905739067390773908739097391073911739127391373914739157391673917739187391973920739217392273923739247392573926739277392873929739307393173932739337393473935739367393773938739397394073941739427394373944739457394673947739487394973950739517395273953739547395573956739577395873959739607396173962739637396473965739667396773968739697397073971739727397373974739757397673977739787397973980739817398273983739847398573986739877398873989739907399173992739937399473995739967399773998739997400074001740027400374004740057400674007740087400974010740117401274013740147401574016740177401874019740207402174022740237402474025740267402774028740297403074031740327403374034740357403674037740387403974040740417404274043740447404574046740477404874049740507405174052740537405474055740567405774058740597406074061740627406374064740657406674067740687406974070740717407274073740747407574076740777407874079740807408174082740837408474085740867408774088740897409074091740927409374094740957409674097740987409974100741017410274103741047410574106741077410874109741107411174112741137411474115741167411774118741197412074121741227412374124741257412674127741287412974130741317413274133741347413574136741377413874139741407414174142741437414474145741467414774148741497415074151741527415374154741557415674157741587415974160741617416274163741647416574166741677416874169741707417174172741737417474175741767417774178741797418074181741827418374184741857418674187741887418974190741917419274193741947419574196741977419874199742007420174202742037420474205742067420774208742097421074211742127421374214742157421674217742187421974220742217422274223742247422574226742277422874229742307423174232742337423474235742367423774238742397424074241742427424374244742457424674247742487424974250742517425274253742547425574256742577425874259742607426174262742637426474265742667426774268742697427074271742727427374274742757427674277742787427974280742817428274283742847428574286742877428874289742907429174292742937429474295742967429774298742997430074301743027430374304743057430674307743087430974310743117431274313743147431574316743177431874319743207432174322743237432474325743267432774328743297433074331743327433374334743357433674337743387433974340743417434274343743447434574346743477434874349743507435174352743537435474355743567435774358743597436074361743627436374364743657436674367743687436974370743717437274373743747437574376743777437874379743807438174382743837438474385743867438774388743897439074391743927439374394743957439674397743987439974400744017440274403744047440574406744077440874409744107441174412744137441474415744167441774418744197442074421744227442374424744257442674427744287442974430744317443274433744347443574436744377443874439744407444174442744437444474445744467444774448744497445074451744527445374454744557445674457744587445974460744617446274463744647446574466744677446874469744707447174472744737447474475744767447774478744797448074481744827448374484744857448674487744887448974490744917449274493744947449574496744977449874499745007450174502745037450474505745067450774508745097451074511745127451374514745157451674517745187451974520745217452274523745247452574526745277452874529745307453174532745337453474535745367453774538745397454074541745427454374544745457454674547745487454974550745517455274553745547455574556745577455874559745607456174562745637456474565745667456774568745697457074571745727457374574745757457674577745787457974580745817458274583745847458574586745877458874589745907459174592745937459474595745967459774598745997460074601746027460374604746057460674607746087460974610746117461274613746147461574616746177461874619746207462174622746237462474625746267462774628746297463074631746327463374634746357463674637746387463974640746417464274643746447464574646746477464874649746507465174652746537465474655746567465774658746597466074661746627466374664746657466674667746687466974670746717467274673746747467574676746777467874679746807468174682746837468474685746867468774688746897469074691746927469374694746957469674697746987469974700747017470274703747047470574706747077470874709747107471174712747137471474715747167471774718747197472074721747227472374724747257472674727747287472974730747317473274733747347473574736747377473874739747407474174742747437474474745747467474774748747497475074751747527475374754747557475674757747587475974760747617476274763747647476574766747677476874769747707477174772747737477474775747767477774778747797478074781747827478374784747857478674787747887478974790747917479274793747947479574796747977479874799748007480174802748037480474805748067480774808748097481074811748127481374814748157481674817748187481974820748217482274823748247482574826748277482874829748307483174832748337483474835748367483774838748397484074841748427484374844748457484674847748487484974850748517485274853748547485574856748577485874859748607486174862748637486474865748667486774868748697487074871748727487374874748757487674877748787487974880748817488274883748847488574886748877488874889748907489174892748937489474895748967489774898748997490074901749027490374904749057490674907749087490974910749117491274913749147491574916749177491874919749207492174922749237492474925749267492774928749297493074931749327493374934749357493674937749387493974940749417494274943749447494574946749477494874949749507495174952749537495474955749567495774958749597496074961749627496374964749657496674967749687496974970749717497274973749747497574976749777497874979749807498174982749837498474985749867498774988749897499074991749927499374994749957499674997749987499975000750017500275003750047500575006750077500875009750107501175012750137501475015750167501775018750197502075021750227502375024750257502675027750287502975030750317503275033750347503575036750377503875039750407504175042750437504475045750467504775048750497505075051750527505375054750557505675057750587505975060750617506275063750647506575066750677506875069750707507175072750737507475075750767507775078750797508075081750827508375084750857508675087750887508975090750917509275093750947509575096750977509875099751007510175102751037510475105751067510775108751097511075111751127511375114751157511675117751187511975120751217512275123751247512575126751277512875129751307513175132751337513475135751367513775138751397514075141751427514375144751457514675147751487514975150751517515275153751547515575156751577515875159751607516175162751637516475165751667516775168751697517075171751727517375174751757517675177751787517975180751817518275183751847518575186751877518875189751907519175192751937519475195751967519775198751997520075201752027520375204752057520675207752087520975210752117521275213752147521575216752177521875219752207522175222752237522475225752267522775228752297523075231752327523375234752357523675237752387523975240752417524275243752447524575246752477524875249752507525175252752537525475255752567525775258752597526075261752627526375264752657526675267752687526975270752717527275273752747527575276752777527875279752807528175282752837528475285752867528775288752897529075291752927529375294752957529675297752987529975300753017530275303753047530575306753077530875309753107531175312753137531475315753167531775318753197532075321753227532375324753257532675327753287532975330753317533275333753347533575336753377533875339753407534175342753437534475345753467534775348753497535075351753527535375354753557535675357753587535975360753617536275363753647536575366753677536875369753707537175372753737537475375753767537775378753797538075381753827538375384753857538675387753887538975390753917539275393753947539575396753977539875399754007540175402754037540475405754067540775408754097541075411754127541375414754157541675417754187541975420754217542275423754247542575426754277542875429754307543175432754337543475435754367543775438754397544075441754427544375444754457544675447754487544975450754517545275453754547545575456754577545875459754607546175462754637546475465754667546775468754697547075471754727547375474754757547675477754787547975480754817548275483754847548575486754877548875489754907549175492754937549475495754967549775498754997550075501755027550375504755057550675507755087550975510755117551275513755147551575516755177551875519755207552175522755237552475525755267552775528755297553075531755327553375534755357553675537755387553975540755417554275543755447554575546755477554875549755507555175552755537555475555755567555775558755597556075561755627556375564755657556675567755687556975570755717557275573755747557575576755777557875579755807558175582755837558475585755867558775588755897559075591755927559375594755957559675597755987559975600756017560275603756047560575606756077560875609756107561175612756137561475615756167561775618756197562075621756227562375624756257562675627756287562975630756317563275633756347563575636756377563875639756407564175642756437564475645756467564775648756497565075651756527565375654756557565675657756587565975660756617566275663756647566575666756677566875669756707567175672756737567475675756767567775678756797568075681756827568375684756857568675687756887568975690756917569275693756947569575696756977569875699757007570175702757037570475705757067570775708757097571075711757127571375714757157571675717757187571975720757217572275723757247572575726757277572875729757307573175732757337573475735757367573775738757397574075741757427574375744757457574675747757487574975750757517575275753757547575575756757577575875759757607576175762757637576475765757667576775768757697577075771757727577375774757757577675777757787577975780757817578275783757847578575786757877578875789757907579175792757937579475795757967579775798757997580075801758027580375804758057580675807758087580975810758117581275813758147581575816758177581875819758207582175822758237582475825758267582775828758297583075831758327583375834758357583675837758387583975840758417584275843758447584575846758477584875849758507585175852758537585475855758567585775858758597586075861758627586375864758657586675867758687586975870758717587275873758747587575876758777587875879758807588175882758837588475885758867588775888758897589075891758927589375894758957589675897758987589975900759017590275903759047590575906759077590875909759107591175912759137591475915759167591775918759197592075921759227592375924759257592675927759287592975930759317593275933759347593575936759377593875939759407594175942759437594475945759467594775948759497595075951759527595375954759557595675957759587595975960759617596275963759647596575966759677596875969759707597175972759737597475975759767597775978759797598075981759827598375984759857598675987759887598975990759917599275993759947599575996759977599875999760007600176002760037600476005760067600776008760097601076011760127601376014760157601676017760187601976020760217602276023760247602576026760277602876029760307603176032760337603476035760367603776038760397604076041760427604376044760457604676047760487604976050760517605276053760547605576056760577605876059760607606176062760637606476065760667606776068760697607076071760727607376074760757607676077760787607976080760817608276083760847608576086760877608876089760907609176092760937609476095760967609776098760997610076101761027610376104761057610676107761087610976110761117611276113761147611576116761177611876119761207612176122761237612476125761267612776128761297613076131761327613376134761357613676137761387613976140761417614276143761447614576146761477614876149761507615176152761537615476155761567615776158761597616076161761627616376164761657616676167761687616976170761717617276173761747617576176761777617876179761807618176182761837618476185761867618776188761897619076191761927619376194761957619676197761987619976200762017620276203762047620576206762077620876209762107621176212762137621476215762167621776218762197622076221762227622376224762257622676227762287622976230762317623276233762347623576236762377623876239762407624176242762437624476245762467624776248762497625076251762527625376254762557625676257762587625976260762617626276263762647626576266762677626876269762707627176272762737627476275762767627776278762797628076281762827628376284762857628676287762887628976290762917629276293762947629576296762977629876299763007630176302763037630476305763067630776308763097631076311763127631376314763157631676317763187631976320763217632276323763247632576326763277632876329763307633176332763337633476335763367633776338763397634076341763427634376344763457634676347763487634976350763517635276353763547635576356763577635876359763607636176362763637636476365763667636776368763697637076371763727637376374763757637676377763787637976380763817638276383763847638576386763877638876389763907639176392763937639476395763967639776398763997640076401764027640376404764057640676407764087640976410764117641276413764147641576416764177641876419764207642176422764237642476425764267642776428764297643076431764327643376434764357643676437764387643976440764417644276443764447644576446764477644876449764507645176452764537645476455764567645776458764597646076461764627646376464764657646676467764687646976470764717647276473764747647576476764777647876479764807648176482764837648476485764867648776488764897649076491764927649376494764957649676497764987649976500765017650276503765047650576506765077650876509765107651176512765137651476515765167651776518765197652076521765227652376524765257652676527765287652976530765317653276533765347653576536765377653876539765407654176542765437654476545765467654776548765497655076551765527655376554765557655676557765587655976560765617656276563765647656576566765677656876569765707657176572765737657476575765767657776578765797658076581765827658376584765857658676587765887658976590765917659276593765947659576596765977659876599766007660176602766037660476605766067660776608766097661076611766127661376614766157661676617766187661976620766217662276623766247662576626766277662876629766307663176632766337663476635766367663776638766397664076641766427664376644766457664676647766487664976650766517665276653766547665576656766577665876659766607666176662766637666476665766667666776668766697667076671766727667376674766757667676677766787667976680766817668276683766847668576686766877668876689766907669176692766937669476695766967669776698766997670076701767027670376704767057670676707767087670976710767117671276713767147671576716767177671876719767207672176722767237672476725767267672776728767297673076731767327673376734767357673676737767387673976740767417674276743767447674576746767477674876749767507675176752767537675476755767567675776758767597676076761767627676376764767657676676767767687676976770767717677276773767747677576776767777677876779767807678176782767837678476785767867678776788767897679076791767927679376794767957679676797767987679976800768017680276803768047680576806768077680876809768107681176812768137681476815768167681776818768197682076821768227682376824768257682676827768287682976830768317683276833768347683576836768377683876839768407684176842768437684476845768467684776848768497685076851768527685376854768557685676857768587685976860768617686276863768647686576866768677686876869768707687176872768737687476875768767687776878768797688076881768827688376884768857688676887768887688976890768917689276893768947689576896768977689876899769007690176902769037690476905769067690776908769097691076911769127691376914769157691676917769187691976920769217692276923769247692576926769277692876929769307693176932769337693476935769367693776938769397694076941769427694376944769457694676947769487694976950769517695276953769547695576956769577695876959769607696176962769637696476965769667696776968769697697076971769727697376974769757697676977769787697976980769817698276983769847698576986769877698876989769907699176992769937699476995769967699776998769997700077001770027700377004770057700677007770087700977010770117701277013770147701577016770177701877019770207702177022770237702477025770267702777028770297703077031770327703377034770357703677037770387703977040770417704277043770447704577046770477704877049770507705177052770537705477055770567705777058770597706077061770627706377064770657706677067770687706977070770717707277073770747707577076770777707877079770807708177082770837708477085770867708777088770897709077091770927709377094770957709677097770987709977100771017710277103771047710577106771077710877109771107711177112771137711477115771167711777118771197712077121771227712377124771257712677127771287712977130771317713277133771347713577136771377713877139771407714177142771437714477145771467714777148771497715077151771527715377154771557715677157771587715977160771617716277163771647716577166771677716877169771707717177172771737717477175771767717777178771797718077181771827718377184771857718677187771887718977190771917719277193771947719577196771977719877199772007720177202772037720477205772067720777208772097721077211772127721377214772157721677217772187721977220772217722277223772247722577226772277722877229772307723177232772337723477235772367723777238772397724077241772427724377244772457724677247772487724977250772517725277253772547725577256772577725877259772607726177262772637726477265772667726777268772697727077271772727727377274772757727677277772787727977280772817728277283772847728577286772877728877289772907729177292772937729477295772967729777298772997730077301773027730377304773057730677307773087730977310773117731277313773147731577316773177731877319773207732177322773237732477325773267732777328773297733077331773327733377334773357733677337773387733977340773417734277343773447734577346773477734877349773507735177352773537735477355773567735777358773597736077361773627736377364773657736677367773687736977370773717737277373773747737577376773777737877379773807738177382773837738477385773867738777388773897739077391773927739377394773957739677397773987739977400774017740277403774047740577406774077740877409774107741177412774137741477415774167741777418774197742077421774227742377424774257742677427774287742977430774317743277433774347743577436774377743877439774407744177442774437744477445774467744777448774497745077451774527745377454774557745677457774587745977460774617746277463774647746577466774677746877469774707747177472774737747477475774767747777478774797748077481774827748377484774857748677487774887748977490774917749277493774947749577496774977749877499775007750177502775037750477505775067750777508775097751077511775127751377514775157751677517775187751977520775217752277523775247752577526775277752877529775307753177532775337753477535775367753777538775397754077541775427754377544775457754677547775487754977550775517755277553775547755577556775577755877559775607756177562775637756477565775667756777568775697757077571775727757377574775757757677577775787757977580775817758277583775847758577586775877758877589775907759177592775937759477595775967759777598775997760077601776027760377604776057760677607776087760977610776117761277613776147761577616776177761877619776207762177622776237762477625776267762777628776297763077631776327763377634776357763677637776387763977640776417764277643776447764577646776477764877649776507765177652776537765477655776567765777658776597766077661776627766377664776657766677667776687766977670776717767277673776747767577676776777767877679776807768177682776837768477685776867768777688776897769077691776927769377694776957769677697776987769977700777017770277703777047770577706777077770877709777107771177712777137771477715777167771777718777197772077721777227772377724777257772677727777287772977730777317773277733777347773577736777377773877739777407774177742777437774477745777467774777748777497775077751777527775377754777557775677757777587775977760777617776277763777647776577766777677776877769777707777177772777737777477775777767777777778777797778077781777827778377784777857778677787777887778977790777917779277793777947779577796777977779877799778007780177802778037780477805778067780777808778097781077811778127781377814778157781677817778187781977820778217782277823778247782577826778277782877829778307783177832778337783477835778367783777838778397784077841778427784377844778457784677847778487784977850778517785277853778547785577856778577785877859778607786177862778637786477865778667786777868778697787077871778727787377874778757787677877778787787977880778817788277883778847788577886778877788877889778907789177892778937789477895778967789777898778997790077901779027790377904779057790677907779087790977910779117791277913779147791577916779177791877919779207792177922779237792477925779267792777928779297793077931779327793377934779357793677937779387793977940779417794277943779447794577946779477794877949779507795177952779537795477955779567795777958779597796077961779627796377964779657796677967779687796977970779717797277973779747797577976779777797877979779807798177982779837798477985779867798777988779897799077991779927799377994779957799677997779987799978000780017800278003780047800578006780077800878009780107801178012780137801478015780167801778018780197802078021780227802378024780257802678027780287802978030780317803278033780347803578036780377803878039780407804178042780437804478045780467804778048780497805078051780527805378054780557805678057780587805978060780617806278063780647806578066780677806878069780707807178072780737807478075780767807778078780797808078081780827808378084780857808678087780887808978090780917809278093780947809578096780977809878099781007810178102781037810478105781067810778108781097811078111781127811378114781157811678117781187811978120781217812278123781247812578126781277812878129781307813178132781337813478135781367813778138781397814078141781427814378144781457814678147781487814978150781517815278153781547815578156781577815878159781607816178162781637816478165781667816778168781697817078171781727817378174781757817678177781787817978180781817818278183781847818578186781877818878189781907819178192781937819478195781967819778198781997820078201782027820378204782057820678207782087820978210782117821278213782147821578216782177821878219782207822178222782237822478225782267822778228782297823078231782327823378234782357823678237782387823978240782417824278243782447824578246782477824878249782507825178252782537825478255782567825778258782597826078261782627826378264782657826678267782687826978270782717827278273782747827578276782777827878279782807828178282782837828478285782867828778288782897829078291782927829378294782957829678297782987829978300783017830278303783047830578306783077830878309783107831178312783137831478315783167831778318783197832078321783227832378324783257832678327783287832978330783317833278333783347833578336783377833878339783407834178342783437834478345783467834778348783497835078351783527835378354783557835678357783587835978360783617836278363783647836578366783677836878369783707837178372783737837478375783767837778378783797838078381783827838378384783857838678387783887838978390783917839278393783947839578396783977839878399784007840178402784037840478405784067840778408784097841078411784127841378414784157841678417784187841978420784217842278423784247842578426784277842878429784307843178432784337843478435784367843778438784397844078441784427844378444784457844678447784487844978450784517845278453784547845578456784577845878459784607846178462784637846478465784667846778468784697847078471784727847378474784757847678477784787847978480784817848278483784847848578486784877848878489784907849178492784937849478495784967849778498784997850078501785027850378504785057850678507785087850978510785117851278513785147851578516785177851878519785207852178522785237852478525785267852778528785297853078531785327853378534785357853678537785387853978540785417854278543785447854578546785477854878549785507855178552785537855478555785567855778558785597856078561785627856378564785657856678567785687856978570785717857278573785747857578576785777857878579785807858178582785837858478585785867858778588785897859078591785927859378594785957859678597785987859978600786017860278603786047860578606786077860878609786107861178612786137861478615786167861778618786197862078621786227862378624786257862678627786287862978630786317863278633786347863578636786377863878639786407864178642786437864478645786467864778648786497865078651786527865378654786557865678657786587865978660786617866278663786647866578666786677866878669786707867178672786737867478675786767867778678786797868078681786827868378684786857868678687786887868978690786917869278693786947869578696786977869878699787007870178702787037870478705787067870778708787097871078711787127871378714787157871678717787187871978720787217872278723787247872578726787277872878729787307873178732787337873478735787367873778738787397874078741787427874378744787457874678747787487874978750787517875278753787547875578756787577875878759787607876178762787637876478765787667876778768787697877078771787727877378774787757877678777787787877978780787817878278783787847878578786787877878878789787907879178792787937879478795787967879778798787997880078801788027880378804788057880678807788087880978810788117881278813788147881578816788177881878819788207882178822788237882478825788267882778828788297883078831788327883378834788357883678837788387883978840788417884278843788447884578846788477884878849788507885178852788537885478855788567885778858788597886078861788627886378864788657886678867788687886978870788717887278873788747887578876788777887878879788807888178882788837888478885788867888778888788897889078891788927889378894788957889678897788987889978900789017890278903789047890578906789077890878909789107891178912789137891478915789167891778918789197892078921789227892378924789257892678927789287892978930789317893278933789347893578936789377893878939789407894178942789437894478945789467894778948789497895078951789527895378954789557895678957789587895978960789617896278963789647896578966789677896878969789707897178972789737897478975789767897778978789797898078981789827898378984789857898678987789887898978990789917899278993789947899578996789977899878999790007900179002790037900479005790067900779008790097901079011790127901379014790157901679017790187901979020790217902279023790247902579026790277902879029790307903179032790337903479035790367903779038790397904079041790427904379044790457904679047790487904979050790517905279053790547905579056790577905879059790607906179062790637906479065790667906779068790697907079071790727907379074790757907679077790787907979080790817908279083790847908579086790877908879089790907909179092790937909479095790967909779098790997910079101791027910379104791057910679107791087910979110791117911279113791147911579116791177911879119791207912179122791237912479125791267912779128791297913079131791327913379134791357913679137791387913979140791417914279143791447914579146791477914879149791507915179152791537915479155791567915779158791597916079161791627916379164791657916679167791687916979170791717917279173791747917579176791777917879179791807918179182791837918479185791867918779188791897919079191791927919379194791957919679197791987919979200792017920279203792047920579206792077920879209792107921179212792137921479215792167921779218792197922079221792227922379224792257922679227792287922979230792317923279233792347923579236792377923879239792407924179242792437924479245792467924779248792497925079251792527925379254792557925679257792587925979260792617926279263792647926579266792677926879269792707927179272792737927479275792767927779278792797928079281792827928379284792857928679287792887928979290792917929279293792947929579296792977929879299793007930179302793037930479305793067930779308793097931079311793127931379314793157931679317793187931979320793217932279323793247932579326793277932879329793307933179332793337933479335793367933779338793397934079341793427934379344793457934679347793487934979350793517935279353793547935579356793577935879359793607936179362793637936479365793667936779368793697937079371793727937379374793757937679377793787937979380793817938279383793847938579386793877938879389793907939179392793937939479395793967939779398793997940079401794027940379404794057940679407794087940979410794117941279413794147941579416794177941879419794207942179422794237942479425794267942779428794297943079431794327943379434794357943679437794387943979440794417944279443794447944579446794477944879449794507945179452794537945479455794567945779458794597946079461794627946379464794657946679467794687946979470794717947279473794747947579476794777947879479794807948179482794837948479485794867948779488794897949079491794927949379494794957949679497794987949979500795017950279503795047950579506795077950879509795107951179512795137951479515795167951779518795197952079521795227952379524795257952679527795287952979530795317953279533795347953579536795377953879539795407954179542795437954479545795467954779548795497955079551795527955379554795557955679557795587955979560795617956279563795647956579566795677956879569795707957179572795737957479575795767957779578795797958079581795827958379584795857958679587795887958979590795917959279593795947959579596795977959879599796007960179602796037960479605796067960779608796097961079611796127961379614796157961679617796187961979620796217962279623796247962579626796277962879629796307963179632796337963479635796367963779638796397964079641796427964379644796457964679647796487964979650796517965279653796547965579656796577965879659796607966179662796637966479665796667966779668796697967079671796727967379674796757967679677796787967979680796817968279683796847968579686796877968879689796907969179692796937969479695796967969779698796997970079701797027970379704797057970679707797087970979710797117971279713797147971579716797177971879719797207972179722797237972479725797267972779728797297973079731797327973379734797357973679737797387973979740797417974279743797447974579746797477974879749797507975179752797537975479755797567975779758797597976079761797627976379764797657976679767797687976979770797717977279773797747977579776797777977879779797807978179782797837978479785797867978779788797897979079791797927979379794797957979679797797987979979800798017980279803798047980579806798077980879809798107981179812798137981479815798167981779818798197982079821798227982379824798257982679827798287982979830798317983279833798347983579836798377983879839798407984179842798437984479845798467984779848798497985079851798527985379854798557985679857798587985979860798617986279863798647986579866798677986879869798707987179872798737987479875798767987779878798797988079881798827988379884798857988679887798887988979890798917989279893798947989579896798977989879899799007990179902799037990479905799067990779908799097991079911799127991379914799157991679917799187991979920799217992279923799247992579926799277992879929799307993179932799337993479935799367993779938799397994079941799427994379944799457994679947799487994979950799517995279953799547995579956799577995879959799607996179962799637996479965799667996779968799697997079971799727997379974799757997679977799787997979980799817998279983799847998579986799877998879989799907999179992799937999479995799967999779998799998000080001800028000380004800058000680007800088000980010800118001280013800148001580016800178001880019800208002180022800238002480025800268002780028800298003080031800328003380034800358003680037800388003980040800418004280043800448004580046800478004880049800508005180052800538005480055800568005780058800598006080061800628006380064800658006680067800688006980070800718007280073800748007580076800778007880079800808008180082800838008480085800868008780088800898009080091800928009380094800958009680097800988009980100801018010280103801048010580106801078010880109801108011180112801138011480115801168011780118801198012080121801228012380124801258012680127801288012980130801318013280133801348013580136801378013880139801408014180142801438014480145801468014780148801498015080151801528015380154801558015680157801588015980160801618016280163801648016580166801678016880169801708017180172801738017480175801768017780178801798018080181801828018380184801858018680187801888018980190801918019280193801948019580196801978019880199802008020180202802038020480205802068020780208802098021080211802128021380214802158021680217802188021980220802218022280223802248022580226802278022880229802308023180232802338023480235802368023780238802398024080241802428024380244802458024680247802488024980250802518025280253802548025580256802578025880259802608026180262802638026480265802668026780268802698027080271802728027380274802758027680277802788027980280802818028280283802848028580286802878028880289802908029180292802938029480295802968029780298802998030080301803028030380304803058030680307803088030980310803118031280313803148031580316803178031880319803208032180322803238032480325803268032780328803298033080331803328033380334803358033680337803388033980340803418034280343803448034580346803478034880349803508035180352803538035480355803568035780358803598036080361803628036380364803658036680367803688036980370803718037280373803748037580376803778037880379803808038180382803838038480385803868038780388803898039080391803928039380394803958039680397803988039980400804018040280403804048040580406804078040880409804108041180412804138041480415804168041780418804198042080421804228042380424804258042680427804288042980430804318043280433804348043580436804378043880439804408044180442804438044480445804468044780448804498045080451804528045380454804558045680457804588045980460804618046280463804648046580466804678046880469804708047180472804738047480475804768047780478804798048080481804828048380484804858048680487804888048980490804918049280493804948049580496804978049880499805008050180502805038050480505805068050780508805098051080511805128051380514805158051680517805188051980520805218052280523805248052580526805278052880529805308053180532805338053480535805368053780538805398054080541805428054380544805458054680547805488054980550805518055280553805548055580556805578055880559805608056180562805638056480565805668056780568805698057080571805728057380574805758057680577805788057980580805818058280583805848058580586805878058880589805908059180592805938059480595805968059780598805998060080601806028060380604806058060680607806088060980610806118061280613806148061580616806178061880619806208062180622806238062480625806268062780628806298063080631806328063380634806358063680637806388063980640806418064280643806448064580646806478064880649806508065180652806538065480655806568065780658806598066080661806628066380664806658066680667806688066980670806718067280673806748067580676806778067880679806808068180682806838068480685806868068780688806898069080691806928069380694806958069680697806988069980700807018070280703807048070580706807078070880709807108071180712807138071480715807168071780718807198072080721807228072380724807258072680727807288072980730807318073280733807348073580736807378073880739807408074180742807438074480745807468074780748807498075080751807528075380754807558075680757807588075980760807618076280763807648076580766807678076880769807708077180772807738077480775807768077780778807798078080781807828078380784807858078680787807888078980790807918079280793807948079580796807978079880799808008080180802808038080480805808068080780808808098081080811808128081380814808158081680817808188081980820808218082280823808248082580826808278082880829808308083180832808338083480835808368083780838808398084080841808428084380844808458084680847808488084980850808518085280853808548085580856808578085880859808608086180862808638086480865808668086780868808698087080871808728087380874808758087680877808788087980880808818088280883808848088580886808878088880889808908089180892808938089480895808968089780898808998090080901809028090380904809058090680907809088090980910809118091280913809148091580916809178091880919809208092180922809238092480925809268092780928809298093080931809328093380934809358093680937809388093980940809418094280943809448094580946809478094880949809508095180952809538095480955809568095780958809598096080961809628096380964809658096680967809688096980970809718097280973809748097580976809778097880979809808098180982809838098480985809868098780988809898099080991809928099380994809958099680997809988099981000810018100281003810048100581006810078100881009810108101181012810138101481015810168101781018810198102081021810228102381024810258102681027810288102981030810318103281033810348103581036810378103881039810408104181042810438104481045810468104781048810498105081051810528105381054810558105681057810588105981060810618106281063810648106581066810678106881069810708107181072810738107481075810768107781078810798108081081810828108381084810858108681087810888108981090810918109281093810948109581096810978109881099811008110181102811038110481105811068110781108811098111081111811128111381114811158111681117811188111981120811218112281123811248112581126811278112881129811308113181132811338113481135811368113781138811398114081141811428114381144811458114681147811488114981150811518115281153811548115581156811578115881159811608116181162811638116481165811668116781168811698117081171811728117381174811758117681177811788117981180811818118281183811848118581186811878118881189811908119181192811938119481195811968119781198811998120081201812028120381204812058120681207812088120981210812118121281213812148121581216812178121881219812208122181222812238122481225812268122781228812298123081231812328123381234812358123681237812388123981240812418124281243812448124581246812478124881249812508125181252812538125481255812568125781258812598126081261812628126381264812658126681267812688126981270812718127281273812748127581276812778127881279812808128181282812838128481285812868128781288812898129081291812928129381294812958129681297812988129981300813018130281303813048130581306813078130881309813108131181312813138131481315813168131781318813198132081321813228132381324813258132681327813288132981330813318133281333813348133581336813378133881339813408134181342813438134481345813468134781348813498135081351813528135381354813558135681357813588135981360813618136281363813648136581366813678136881369813708137181372813738137481375813768137781378813798138081381813828138381384813858138681387813888138981390813918139281393813948139581396813978139881399814008140181402814038140481405814068140781408814098141081411814128141381414814158141681417814188141981420814218142281423814248142581426814278142881429814308143181432814338143481435814368143781438814398144081441814428144381444814458144681447814488144981450814518145281453814548145581456814578145881459814608146181462814638146481465814668146781468814698147081471814728147381474814758147681477814788147981480814818148281483814848148581486814878148881489814908149181492814938149481495814968149781498814998150081501815028150381504815058150681507815088150981510815118151281513815148151581516815178151881519815208152181522815238152481525815268152781528815298153081531815328153381534815358153681537815388153981540815418154281543815448154581546815478154881549815508155181552815538155481555815568155781558815598156081561815628156381564815658156681567815688156981570815718157281573815748157581576815778157881579815808158181582815838158481585815868158781588815898159081591815928159381594815958159681597815988159981600816018160281603816048160581606816078160881609816108161181612816138161481615816168161781618816198162081621816228162381624816258162681627816288162981630816318163281633816348163581636816378163881639816408164181642816438164481645816468164781648816498165081651816528165381654816558165681657816588165981660816618166281663816648166581666816678166881669816708167181672816738167481675816768167781678816798168081681816828168381684816858168681687816888168981690816918169281693816948169581696816978169881699817008170181702817038170481705817068170781708817098171081711817128171381714817158171681717817188171981720817218172281723817248172581726817278172881729817308173181732817338173481735817368173781738817398174081741817428174381744817458174681747817488174981750817518175281753817548175581756817578175881759817608176181762817638176481765817668176781768817698177081771817728177381774817758177681777817788177981780817818178281783817848178581786817878178881789817908179181792817938179481795817968179781798817998180081801818028180381804818058180681807818088180981810818118181281813818148181581816818178181881819818208182181822818238182481825818268182781828818298183081831818328183381834818358183681837818388183981840818418184281843818448184581846818478184881849818508185181852818538185481855818568185781858818598186081861818628186381864818658186681867818688186981870818718187281873818748187581876818778187881879818808188181882818838188481885818868188781888818898189081891818928189381894818958189681897818988189981900819018190281903819048190581906819078190881909819108191181912819138191481915819168191781918819198192081921819228192381924819258192681927819288192981930819318193281933819348193581936819378193881939819408194181942819438194481945819468194781948819498195081951819528195381954819558195681957819588195981960819618196281963819648196581966819678196881969819708197181972819738197481975819768197781978819798198081981819828198381984819858198681987819888198981990819918199281993819948199581996819978199881999820008200182002820038200482005820068200782008820098201082011820128201382014820158201682017820188201982020820218202282023820248202582026820278202882029820308203182032820338203482035820368203782038820398204082041820428204382044820458204682047820488204982050820518205282053820548205582056820578205882059820608206182062820638206482065820668206782068820698207082071820728207382074820758207682077820788207982080820818208282083820848208582086820878208882089820908209182092820938209482095820968209782098820998210082101821028210382104821058210682107821088210982110821118211282113821148211582116821178211882119821208212182122821238212482125821268212782128821298213082131821328213382134821358213682137821388213982140821418214282143821448214582146821478214882149821508215182152821538215482155821568215782158821598216082161821628216382164821658216682167821688216982170821718217282173821748217582176821778217882179821808218182182821838218482185821868218782188821898219082191821928219382194821958219682197821988219982200822018220282203822048220582206822078220882209822108221182212822138221482215822168221782218822198222082221822228222382224822258222682227822288222982230822318223282233822348223582236822378223882239822408224182242822438224482245822468224782248822498225082251822528225382254822558225682257822588225982260822618226282263822648226582266822678226882269822708227182272822738227482275822768227782278822798228082281822828228382284822858228682287822888228982290822918229282293822948229582296822978229882299823008230182302823038230482305823068230782308823098231082311823128231382314823158231682317823188231982320823218232282323823248232582326823278232882329823308233182332823338233482335823368233782338823398234082341823428234382344823458234682347823488234982350823518235282353823548235582356823578235882359823608236182362823638236482365823668236782368823698237082371823728237382374823758237682377823788237982380823818238282383823848238582386823878238882389823908239182392823938239482395823968239782398823998240082401824028240382404824058240682407824088240982410824118241282413824148241582416824178241882419824208242182422824238242482425824268242782428824298243082431824328243382434824358243682437824388243982440824418244282443824448244582446824478244882449824508245182452824538245482455824568245782458824598246082461824628246382464824658246682467824688246982470824718247282473824748247582476824778247882479824808248182482824838248482485824868248782488824898249082491824928249382494824958249682497824988249982500825018250282503825048250582506825078250882509825108251182512825138251482515825168251782518825198252082521825228252382524825258252682527825288252982530825318253282533825348253582536825378253882539825408254182542825438254482545825468254782548825498255082551825528255382554825558255682557825588255982560825618256282563825648256582566825678256882569825708257182572825738257482575825768257782578825798258082581825828258382584825858258682587825888258982590825918259282593825948259582596825978259882599826008260182602826038260482605826068260782608826098261082611826128261382614826158261682617826188261982620826218262282623826248262582626826278262882629826308263182632826338263482635826368263782638826398264082641826428264382644826458264682647826488264982650826518265282653826548265582656826578265882659826608266182662826638266482665826668266782668826698267082671826728267382674826758267682677826788267982680826818268282683826848268582686826878268882689826908269182692826938269482695826968269782698826998270082701827028270382704827058270682707827088270982710827118271282713827148271582716827178271882719827208272182722827238272482725827268272782728827298273082731827328273382734827358273682737827388273982740827418274282743827448274582746827478274882749827508275182752827538275482755827568275782758827598276082761827628276382764827658276682767827688276982770827718277282773827748277582776827778277882779827808278182782827838278482785827868278782788827898279082791827928279382794827958279682797827988279982800828018280282803828048280582806828078280882809828108281182812828138281482815828168281782818828198282082821828228282382824828258282682827828288282982830828318283282833828348283582836828378283882839828408284182842828438284482845828468284782848828498285082851828528285382854828558285682857828588285982860828618286282863828648286582866828678286882869828708287182872828738287482875828768287782878828798288082881828828288382884828858288682887828888288982890828918289282893828948289582896828978289882899829008290182902829038290482905829068290782908829098291082911829128291382914829158291682917829188291982920829218292282923829248292582926829278292882929829308293182932829338293482935829368293782938829398294082941829428294382944829458294682947829488294982950829518295282953829548295582956829578295882959829608296182962829638296482965829668296782968829698297082971829728297382974829758297682977829788297982980829818298282983829848298582986829878298882989829908299182992829938299482995829968299782998829998300083001830028300383004830058300683007830088300983010830118301283013830148301583016830178301883019830208302183022830238302483025830268302783028830298303083031830328303383034830358303683037830388303983040830418304283043830448304583046830478304883049830508305183052830538305483055830568305783058830598306083061830628306383064830658306683067830688306983070830718307283073830748307583076830778307883079830808308183082830838308483085830868308783088830898309083091830928309383094830958309683097830988309983100831018310283103831048310583106831078310883109831108311183112831138311483115831168311783118831198312083121831228312383124831258312683127831288312983130831318313283133831348313583136831378313883139831408314183142831438314483145831468314783148831498315083151831528315383154831558315683157831588315983160831618316283163831648316583166831678316883169831708317183172831738317483175831768317783178831798318083181831828318383184831858318683187831888318983190831918319283193831948319583196831978319883199832008320183202832038320483205832068320783208832098321083211832128321383214832158321683217832188321983220832218322283223832248322583226832278322883229832308323183232832338323483235832368323783238832398324083241832428324383244832458324683247832488324983250832518325283253832548325583256832578325883259832608326183262832638326483265832668326783268832698327083271832728327383274832758327683277832788327983280832818328283283832848328583286832878328883289832908329183292832938329483295832968329783298832998330083301833028330383304833058330683307833088330983310833118331283313833148331583316833178331883319833208332183322833238332483325833268332783328833298333083331833328333383334833358333683337833388333983340833418334283343833448334583346833478334883349833508335183352833538335483355833568335783358833598336083361833628336383364833658336683367833688336983370833718337283373833748337583376833778337883379833808338183382833838338483385833868338783388833898339083391833928339383394833958339683397833988339983400834018340283403834048340583406834078340883409834108341183412834138341483415834168341783418834198342083421834228342383424834258342683427834288342983430834318343283433834348343583436834378343883439834408344183442834438344483445834468344783448834498345083451834528345383454834558345683457834588345983460834618346283463834648346583466834678346883469834708347183472834738347483475834768347783478834798348083481834828348383484834858348683487834888348983490834918349283493834948349583496834978349883499835008350183502835038350483505835068350783508835098351083511835128351383514835158351683517835188351983520835218352283523835248352583526835278352883529835308353183532835338353483535835368353783538835398354083541835428354383544835458354683547835488354983550835518355283553835548355583556835578355883559835608356183562835638356483565835668356783568835698357083571835728357383574835758357683577835788357983580835818358283583835848358583586835878358883589835908359183592835938359483595835968359783598835998360083601836028360383604836058360683607836088360983610836118361283613836148361583616836178361883619836208362183622836238362483625836268362783628836298363083631836328363383634836358363683637836388363983640836418364283643836448364583646836478364883649836508365183652836538365483655836568365783658836598366083661836628366383664836658366683667836688366983670836718367283673836748367583676836778367883679836808368183682836838368483685836868368783688836898369083691836928369383694836958369683697836988369983700837018370283703837048370583706837078370883709837108371183712837138371483715837168371783718837198372083721837228372383724837258372683727837288372983730837318373283733837348373583736837378373883739837408374183742837438374483745837468374783748837498375083751837528375383754837558375683757837588375983760837618376283763837648376583766837678376883769837708377183772837738377483775837768377783778837798378083781837828378383784837858378683787837888378983790837918379283793837948379583796837978379883799838008380183802838038380483805838068380783808838098381083811838128381383814838158381683817838188381983820838218382283823838248382583826838278382883829838308383183832838338383483835838368383783838838398384083841838428384383844838458384683847838488384983850838518385283853838548385583856838578385883859838608386183862838638386483865838668386783868838698387083871838728387383874838758387683877838788387983880838818388283883838848388583886838878388883889838908389183892838938389483895838968389783898838998390083901839028390383904839058390683907839088390983910839118391283913839148391583916839178391883919839208392183922839238392483925839268392783928839298393083931839328393383934839358393683937839388393983940839418394283943839448394583946839478394883949839508395183952839538395483955839568395783958839598396083961839628396383964839658396683967839688396983970839718397283973839748397583976839778397883979839808398183982839838398483985839868398783988839898399083991839928399383994839958399683997839988399984000840018400284003840048400584006840078400884009840108401184012840138401484015840168401784018840198402084021840228402384024840258402684027840288402984030840318403284033840348403584036840378403884039840408404184042840438404484045840468404784048840498405084051840528405384054840558405684057840588405984060840618406284063840648406584066840678406884069840708407184072840738407484075840768407784078840798408084081840828408384084840858408684087840888408984090840918409284093840948409584096840978409884099841008410184102841038410484105841068410784108841098411084111841128411384114841158411684117841188411984120841218412284123841248412584126841278412884129841308413184132841338413484135841368413784138841398414084141841428414384144841458414684147841488414984150841518415284153841548415584156841578415884159841608416184162841638416484165841668416784168841698417084171841728417384174841758417684177841788417984180841818418284183841848418584186841878418884189841908419184192841938419484195841968419784198841998420084201842028420384204842058420684207842088420984210842118421284213842148421584216842178421884219842208422184222842238422484225842268422784228842298423084231842328423384234842358423684237842388423984240842418424284243842448424584246842478424884249842508425184252842538425484255842568425784258842598426084261842628426384264842658426684267842688426984270842718427284273842748427584276842778427884279842808428184282842838428484285842868428784288842898429084291842928429384294842958429684297842988429984300843018430284303843048430584306843078430884309843108431184312843138431484315843168431784318843198432084321843228432384324843258432684327843288432984330843318433284333843348433584336843378433884339843408434184342843438434484345843468434784348843498435084351843528435384354843558435684357843588435984360843618436284363843648436584366843678436884369843708437184372843738437484375843768437784378843798438084381843828438384384843858438684387843888438984390843918439284393843948439584396843978439884399844008440184402844038440484405844068440784408844098441084411844128441384414844158441684417844188441984420844218442284423844248442584426844278442884429844308443184432844338443484435844368443784438844398444084441844428444384444844458444684447844488444984450844518445284453844548445584456844578445884459844608446184462844638446484465844668446784468844698447084471844728447384474844758447684477844788447984480844818448284483844848448584486844878448884489844908449184492844938449484495844968449784498844998450084501845028450384504845058450684507845088450984510845118451284513845148451584516845178451884519845208452184522845238452484525845268452784528845298453084531845328453384534845358453684537845388453984540845418454284543845448454584546845478454884549845508455184552845538455484555845568455784558845598456084561845628456384564845658456684567845688456984570845718457284573845748457584576845778457884579845808458184582845838458484585845868458784588845898459084591845928459384594845958459684597845988459984600846018460284603846048460584606846078460884609846108461184612846138461484615846168461784618846198462084621846228462384624846258462684627846288462984630846318463284633846348463584636846378463884639846408464184642846438464484645846468464784648846498465084651846528465384654846558465684657846588465984660846618466284663846648466584666846678466884669846708467184672846738467484675846768467784678846798468084681846828468384684846858468684687846888468984690846918469284693846948469584696846978469884699847008470184702847038470484705847068470784708847098471084711847128471384714847158471684717847188471984720847218472284723847248472584726847278472884729847308473184732847338473484735847368473784738847398474084741847428474384744847458474684747847488474984750847518475284753847548475584756847578475884759847608476184762847638476484765847668476784768847698477084771847728477384774847758477684777847788477984780847818478284783847848478584786847878478884789847908479184792847938479484795847968479784798847998480084801848028480384804848058480684807848088480984810848118481284813848148481584816848178481884819848208482184822848238482484825848268482784828848298483084831848328483384834848358483684837848388483984840848418484284843848448484584846848478484884849848508485184852848538485484855848568485784858848598486084861848628486384864848658486684867848688486984870848718487284873848748487584876848778487884879848808488184882848838488484885848868488784888848898489084891848928489384894848958489684897848988489984900849018490284903849048490584906849078490884909849108491184912849138491484915849168491784918849198492084921849228492384924849258492684927849288492984930849318493284933849348493584936849378493884939849408494184942849438494484945849468494784948849498495084951849528495384954849558495684957849588495984960849618496284963849648496584966849678496884969849708497184972849738497484975849768497784978849798498084981849828498384984849858498684987849888498984990849918499284993849948499584996849978499884999850008500185002850038500485005850068500785008850098501085011850128501385014850158501685017850188501985020850218502285023850248502585026850278502885029850308503185032850338503485035850368503785038850398504085041850428504385044850458504685047850488504985050850518505285053850548505585056850578505885059850608506185062850638506485065850668506785068850698507085071850728507385074850758507685077850788507985080850818508285083850848508585086850878508885089850908509185092850938509485095850968509785098850998510085101851028510385104851058510685107851088510985110851118511285113851148511585116851178511885119851208512185122851238512485125851268512785128851298513085131851328513385134851358513685137851388513985140851418514285143851448514585146851478514885149851508515185152851538515485155851568515785158851598516085161851628516385164851658516685167851688516985170851718517285173851748517585176851778517885179851808518185182851838518485185851868518785188851898519085191851928519385194851958519685197851988519985200852018520285203852048520585206852078520885209852108521185212852138521485215852168521785218852198522085221852228522385224852258522685227852288522985230852318523285233852348523585236852378523885239852408524185242852438524485245852468524785248852498525085251852528525385254852558525685257852588525985260852618526285263852648526585266852678526885269852708527185272852738527485275852768527785278852798528085281852828528385284852858528685287852888528985290852918529285293852948529585296852978529885299853008530185302853038530485305853068530785308853098531085311853128531385314853158531685317853188531985320853218532285323853248532585326853278532885329853308533185332853338533485335853368533785338853398534085341853428534385344853458534685347853488534985350853518535285353853548535585356853578535885359853608536185362853638536485365853668536785368853698537085371853728537385374853758537685377853788537985380853818538285383853848538585386853878538885389853908539185392853938539485395853968539785398853998540085401854028540385404854058540685407854088540985410854118541285413854148541585416854178541885419854208542185422854238542485425854268542785428854298543085431854328543385434854358543685437854388543985440854418544285443854448544585446854478544885449854508545185452854538545485455854568545785458854598546085461854628546385464854658546685467854688546985470854718547285473854748547585476854778547885479854808548185482854838548485485854868548785488854898549085491854928549385494854958549685497854988549985500855018550285503855048550585506855078550885509855108551185512855138551485515855168551785518855198552085521855228552385524855258552685527855288552985530855318553285533855348553585536855378553885539855408554185542855438554485545855468554785548855498555085551855528555385554855558555685557855588555985560855618556285563855648556585566855678556885569855708557185572855738557485575855768557785578855798558085581855828558385584855858558685587855888558985590855918559285593855948559585596855978559885599856008560185602856038560485605856068560785608856098561085611856128561385614856158561685617856188561985620856218562285623856248562585626856278562885629856308563185632856338563485635856368563785638856398564085641856428564385644856458564685647856488564985650856518565285653856548565585656856578565885659856608566185662856638566485665856668566785668856698567085671856728567385674856758567685677856788567985680856818568285683856848568585686856878568885689856908569185692856938569485695856968569785698856998570085701857028570385704857058570685707857088570985710857118571285713857148571585716857178571885719857208572185722857238572485725857268572785728857298573085731857328573385734857358573685737857388573985740857418574285743857448574585746857478574885749857508575185752857538575485755857568575785758857598576085761857628576385764857658576685767857688576985770857718577285773857748577585776857778577885779857808578185782857838578485785857868578785788857898579085791857928579385794857958579685797857988579985800858018580285803858048580585806858078580885809858108581185812858138581485815858168581785818858198582085821858228582385824858258582685827858288582985830858318583285833858348583585836858378583885839858408584185842858438584485845858468584785848858498585085851858528585385854858558585685857858588585985860858618586285863858648586585866858678586885869858708587185872858738587485875858768587785878858798588085881858828588385884858858588685887858888588985890858918589285893858948589585896858978589885899859008590185902859038590485905859068590785908859098591085911859128591385914859158591685917859188591985920859218592285923859248592585926859278592885929859308593185932859338593485935859368593785938859398594085941859428594385944859458594685947859488594985950859518595285953859548595585956859578595885959859608596185962859638596485965859668596785968859698597085971859728597385974859758597685977859788597985980859818598285983859848598585986859878598885989859908599185992859938599485995859968599785998859998600086001860028600386004860058600686007860088600986010860118601286013860148601586016860178601886019860208602186022860238602486025860268602786028860298603086031860328603386034860358603686037860388603986040860418604286043860448604586046860478604886049860508605186052860538605486055860568605786058860598606086061860628606386064860658606686067860688606986070860718607286073860748607586076860778607886079860808608186082860838608486085860868608786088860898609086091860928609386094860958609686097860988609986100861018610286103861048610586106861078610886109861108611186112861138611486115861168611786118861198612086121861228612386124861258612686127861288612986130861318613286133861348613586136861378613886139861408614186142861438614486145861468614786148861498615086151861528615386154861558615686157861588615986160861618616286163861648616586166861678616886169861708617186172861738617486175861768617786178861798618086181861828618386184861858618686187861888618986190861918619286193861948619586196861978619886199862008620186202862038620486205862068620786208862098621086211862128621386214862158621686217862188621986220862218622286223862248622586226862278622886229862308623186232862338623486235862368623786238862398624086241862428624386244862458624686247862488624986250862518625286253862548625586256862578625886259862608626186262862638626486265862668626786268862698627086271862728627386274862758627686277862788627986280862818628286283862848628586286862878628886289862908629186292862938629486295862968629786298862998630086301863028630386304863058630686307863088630986310863118631286313863148631586316863178631886319863208632186322863238632486325863268632786328863298633086331863328633386334863358633686337863388633986340863418634286343863448634586346863478634886349863508635186352863538635486355863568635786358863598636086361863628636386364863658636686367863688636986370863718637286373863748637586376863778637886379863808638186382863838638486385863868638786388863898639086391863928639386394863958639686397863988639986400864018640286403864048640586406864078640886409864108641186412864138641486415864168641786418864198642086421864228642386424864258642686427864288642986430864318643286433864348643586436864378643886439864408644186442864438644486445864468644786448864498645086451864528645386454864558645686457864588645986460864618646286463864648646586466864678646886469864708647186472864738647486475864768647786478864798648086481864828648386484864858648686487864888648986490864918649286493864948649586496864978649886499865008650186502865038650486505865068650786508865098651086511865128651386514865158651686517865188651986520865218652286523865248652586526865278652886529865308653186532865338653486535865368653786538865398654086541865428654386544865458654686547865488654986550865518655286553865548655586556865578655886559865608656186562865638656486565865668656786568865698657086571865728657386574865758657686577865788657986580865818658286583865848658586586865878658886589865908659186592865938659486595865968659786598865998660086601866028660386604866058660686607866088660986610866118661286613866148661586616866178661886619866208662186622866238662486625866268662786628866298663086631866328663386634866358663686637866388663986640866418664286643866448664586646866478664886649866508665186652866538665486655866568665786658866598666086661866628666386664866658666686667866688666986670866718667286673866748667586676866778667886679866808668186682866838668486685866868668786688866898669086691866928669386694866958669686697866988669986700867018670286703867048670586706867078670886709867108671186712867138671486715867168671786718867198672086721867228672386724867258672686727867288672986730867318673286733867348673586736867378673886739867408674186742867438674486745867468674786748867498675086751867528675386754867558675686757867588675986760867618676286763867648676586766867678676886769867708677186772867738677486775867768677786778867798678086781867828678386784867858678686787867888678986790867918679286793867948679586796867978679886799868008680186802868038680486805868068680786808868098681086811868128681386814868158681686817868188681986820868218682286823868248682586826868278682886829868308683186832868338683486835868368683786838868398684086841868428684386844868458684686847868488684986850868518685286853868548685586856868578685886859868608686186862868638686486865868668686786868868698687086871868728687386874868758687686877868788687986880868818688286883868848688586886868878688886889868908689186892868938689486895868968689786898868998690086901869028690386904869058690686907869088690986910869118691286913869148691586916869178691886919869208692186922869238692486925869268692786928869298693086931869328693386934869358693686937869388693986940869418694286943869448694586946869478694886949869508695186952869538695486955869568695786958869598696086961869628696386964869658696686967869688696986970869718697286973869748697586976869778697886979869808698186982869838698486985869868698786988869898699086991869928699386994869958699686997869988699987000870018700287003870048700587006870078700887009870108701187012870138701487015870168701787018870198702087021870228702387024870258702687027870288702987030870318703287033870348703587036870378703887039870408704187042870438704487045870468704787048870498705087051870528705387054870558705687057870588705987060870618706287063870648706587066870678706887069870708707187072870738707487075870768707787078870798708087081870828708387084870858708687087870888708987090870918709287093870948709587096870978709887099871008710187102871038710487105871068710787108871098711087111871128711387114871158711687117871188711987120871218712287123871248712587126871278712887129871308713187132871338713487135871368713787138871398714087141871428714387144871458714687147871488714987150871518715287153871548715587156871578715887159871608716187162871638716487165871668716787168871698717087171871728717387174871758717687177871788717987180871818718287183871848718587186871878718887189871908719187192871938719487195871968719787198871998720087201872028720387204872058720687207872088720987210872118721287213872148721587216872178721887219872208722187222872238722487225872268722787228872298723087231872328723387234872358723687237872388723987240872418724287243872448724587246872478724887249872508725187252872538725487255872568725787258872598726087261872628726387264872658726687267872688726987270872718727287273872748727587276872778727887279872808728187282872838728487285872868728787288872898729087291872928729387294872958729687297872988729987300873018730287303873048730587306873078730887309873108731187312873138731487315873168731787318873198732087321873228732387324873258732687327873288732987330873318733287333873348733587336873378733887339873408734187342873438734487345873468734787348873498735087351873528735387354873558735687357873588735987360873618736287363873648736587366873678736887369873708737187372873738737487375873768737787378873798738087381873828738387384873858738687387873888738987390873918739287393873948739587396873978739887399874008740187402874038740487405874068740787408874098741087411874128741387414874158741687417874188741987420874218742287423874248742587426874278742887429874308743187432874338743487435874368743787438874398744087441874428744387444874458744687447874488744987450874518745287453874548745587456874578745887459874608746187462874638746487465874668746787468874698747087471874728747387474874758747687477874788747987480874818748287483874848748587486874878748887489874908749187492874938749487495874968749787498874998750087501875028750387504875058750687507875088750987510875118751287513875148751587516875178751887519875208752187522875238752487525875268752787528875298753087531875328753387534875358753687537875388753987540875418754287543875448754587546875478754887549875508755187552875538755487555875568755787558875598756087561875628756387564875658756687567875688756987570875718757287573875748757587576875778757887579875808758187582875838758487585875868758787588875898759087591875928759387594875958759687597875988759987600876018760287603876048760587606876078760887609876108761187612876138761487615876168761787618876198762087621876228762387624876258762687627876288762987630876318763287633876348763587636876378763887639876408764187642876438764487645876468764787648876498765087651876528765387654876558765687657876588765987660876618766287663876648766587666876678766887669876708767187672876738767487675876768767787678876798768087681876828768387684876858768687687876888768987690876918769287693876948769587696876978769887699877008770187702877038770487705877068770787708877098771087711877128771387714877158771687717877188771987720877218772287723877248772587726877278772887729877308773187732877338773487735877368773787738877398774087741877428774387744877458774687747877488774987750877518775287753877548775587756877578775887759877608776187762877638776487765877668776787768877698777087771877728777387774877758777687777877788777987780877818778287783877848778587786877878778887789877908779187792877938779487795877968779787798877998780087801878028780387804878058780687807878088780987810878118781287813878148781587816878178781887819878208782187822878238782487825878268782787828878298783087831878328783387834878358783687837878388783987840878418784287843878448784587846878478784887849878508785187852878538785487855878568785787858878598786087861878628786387864878658786687867878688786987870878718787287873878748787587876878778787887879878808788187882878838788487885878868788787888878898789087891878928789387894878958789687897878988789987900879018790287903879048790587906879078790887909879108791187912879138791487915879168791787918879198792087921879228792387924879258792687927879288792987930879318793287933879348793587936879378793887939879408794187942879438794487945879468794787948879498795087951879528795387954879558795687957879588795987960879618796287963879648796587966879678796887969879708797187972879738797487975879768797787978879798798087981879828798387984879858798687987879888798987990879918799287993879948799587996879978799887999880008800188002880038800488005880068800788008880098801088011880128801388014880158801688017880188801988020880218802288023880248802588026880278802888029880308803188032880338803488035880368803788038880398804088041880428804388044880458804688047880488804988050880518805288053880548805588056880578805888059880608806188062880638806488065880668806788068880698807088071880728807388074880758807688077880788807988080880818808288083880848808588086880878808888089880908809188092880938809488095880968809788098880998810088101881028810388104881058810688107881088810988110881118811288113881148811588116881178811888119881208812188122881238812488125881268812788128881298813088131881328813388134881358813688137881388813988140881418814288143881448814588146881478814888149881508815188152881538815488155881568815788158881598816088161881628816388164881658816688167881688816988170881718817288173881748817588176881778817888179881808818188182881838818488185881868818788188881898819088191881928819388194881958819688197881988819988200882018820288203882048820588206882078820888209882108821188212882138821488215882168821788218882198822088221882228822388224882258822688227882288822988230882318823288233882348823588236882378823888239882408824188242882438824488245882468824788248882498825088251882528825388254882558825688257882588825988260882618826288263882648826588266882678826888269882708827188272882738827488275882768827788278882798828088281882828828388284882858828688287882888828988290882918829288293882948829588296882978829888299883008830188302883038830488305883068830788308883098831088311883128831388314883158831688317883188831988320883218832288323883248832588326883278832888329883308833188332883338833488335883368833788338883398834088341883428834388344883458834688347883488834988350883518835288353883548835588356883578835888359883608836188362883638836488365883668836788368883698837088371883728837388374883758837688377883788837988380883818838288383883848838588386883878838888389883908839188392883938839488395883968839788398883998840088401884028840388404884058840688407884088840988410884118841288413884148841588416884178841888419884208842188422884238842488425884268842788428884298843088431884328843388434884358843688437884388843988440884418844288443884448844588446884478844888449884508845188452884538845488455884568845788458884598846088461884628846388464884658846688467884688846988470884718847288473884748847588476884778847888479884808848188482884838848488485884868848788488884898849088491884928849388494884958849688497884988849988500885018850288503885048850588506885078850888509885108851188512885138851488515885168851788518885198852088521885228852388524885258852688527885288852988530885318853288533885348853588536885378853888539885408854188542885438854488545885468854788548885498855088551885528855388554885558855688557885588855988560885618856288563885648856588566885678856888569885708857188572885738857488575885768857788578885798858088581885828858388584885858858688587885888858988590885918859288593885948859588596885978859888599886008860188602886038860488605886068860788608886098861088611886128861388614886158861688617886188861988620886218862288623886248862588626886278862888629886308863188632886338863488635886368863788638886398864088641886428864388644886458864688647886488864988650886518865288653886548865588656886578865888659886608866188662886638866488665886668866788668886698867088671886728867388674886758867688677886788867988680886818868288683886848868588686886878868888689886908869188692886938869488695886968869788698886998870088701887028870388704887058870688707887088870988710887118871288713887148871588716887178871888719887208872188722887238872488725887268872788728887298873088731887328873388734887358873688737887388873988740887418874288743887448874588746887478874888749887508875188752887538875488755887568875788758887598876088761887628876388764887658876688767887688876988770887718877288773887748877588776887778877888779887808878188782887838878488785887868878788788887898879088791887928879388794887958879688797887988879988800888018880288803888048880588806888078880888809888108881188812888138881488815888168881788818888198882088821888228882388824888258882688827888288882988830888318883288833888348883588836888378883888839888408884188842888438884488845888468884788848888498885088851888528885388854888558885688857888588885988860888618886288863888648886588866888678886888869888708887188872888738887488875888768887788878888798888088881888828888388884888858888688887888888888988890888918889288893888948889588896888978889888899889008890188902889038890488905889068890788908889098891088911889128891388914889158891688917889188891988920889218892288923889248892588926889278892888929889308893188932889338893488935889368893788938889398894088941889428894388944889458894688947889488894988950889518895288953889548895588956889578895888959889608896188962889638896488965889668896788968889698897088971889728897388974889758897688977889788897988980889818898288983889848898588986889878898888989889908899188992889938899488995889968899788998889998900089001890028900389004890058900689007890088900989010890118901289013890148901589016890178901889019890208902189022890238902489025890268902789028890298903089031890328903389034890358903689037890388903989040890418904289043890448904589046890478904889049890508905189052890538905489055890568905789058890598906089061890628906389064890658906689067890688906989070890718907289073890748907589076890778907889079890808908189082890838908489085890868908789088890898909089091890928909389094890958909689097890988909989100891018910289103891048910589106891078910889109891108911189112891138911489115891168911789118891198912089121891228912389124891258912689127891288912989130891318913289133891348913589136891378913889139891408914189142891438914489145891468914789148891498915089151891528915389154891558915689157891588915989160891618916289163891648916589166891678916889169891708917189172891738917489175891768917789178891798918089181891828918389184891858918689187891888918989190891918919289193891948919589196891978919889199892008920189202892038920489205892068920789208892098921089211892128921389214892158921689217892188921989220892218922289223892248922589226892278922889229892308923189232892338923489235892368923789238892398924089241892428924389244892458924689247892488924989250892518925289253892548925589256892578925889259892608926189262892638926489265892668926789268892698927089271892728927389274892758927689277892788927989280892818928289283892848928589286892878928889289892908929189292892938929489295892968929789298892998930089301893028930389304893058930689307893088930989310893118931289313893148931589316893178931889319893208932189322893238932489325893268932789328893298933089331893328933389334893358933689337893388933989340893418934289343893448934589346893478934889349893508935189352893538935489355893568935789358893598936089361893628936389364893658936689367893688936989370893718937289373893748937589376893778937889379893808938189382893838938489385893868938789388893898939089391893928939389394893958939689397893988939989400894018940289403894048940589406894078940889409894108941189412894138941489415894168941789418894198942089421894228942389424894258942689427894288942989430894318943289433894348943589436894378943889439894408944189442894438944489445894468944789448894498945089451894528945389454894558945689457894588945989460894618946289463894648946589466894678946889469894708947189472894738947489475894768947789478894798948089481894828948389484894858948689487894888948989490894918949289493894948949589496894978949889499895008950189502895038950489505895068950789508895098951089511895128951389514895158951689517895188951989520895218952289523895248952589526895278952889529895308953189532895338953489535895368953789538895398954089541895428954389544895458954689547895488954989550895518955289553895548955589556895578955889559895608956189562895638956489565895668956789568895698957089571895728957389574895758957689577895788957989580895818958289583895848958589586895878958889589895908959189592895938959489595895968959789598895998960089601896028960389604896058960689607896088960989610896118961289613896148961589616896178961889619896208962189622896238962489625896268962789628896298963089631896328963389634896358963689637896388963989640896418964289643896448964589646896478964889649896508965189652896538965489655896568965789658896598966089661896628966389664896658966689667896688966989670896718967289673896748967589676896778967889679896808968189682896838968489685896868968789688896898969089691896928969389694896958969689697896988969989700897018970289703897048970589706897078970889709897108971189712897138971489715897168971789718897198972089721897228972389724897258972689727897288972989730897318973289733897348973589736897378973889739897408974189742897438974489745897468974789748897498975089751897528975389754897558975689757897588975989760897618976289763897648976589766897678976889769897708977189772897738977489775897768977789778897798978089781897828978389784897858978689787897888978989790897918979289793897948979589796897978979889799898008980189802898038980489805898068980789808898098981089811898128981389814898158981689817898188981989820898218982289823898248982589826898278982889829898308983189832898338983489835898368983789838898398984089841898428984389844898458984689847898488984989850898518985289853898548985589856898578985889859898608986189862898638986489865898668986789868898698987089871898728987389874898758987689877898788987989880898818988289883898848988589886898878988889889898908989189892898938989489895898968989789898898998990089901899028990389904899058990689907899088990989910899118991289913899148991589916899178991889919899208992189922899238992489925899268992789928899298993089931899328993389934899358993689937899388993989940899418994289943899448994589946899478994889949899508995189952899538995489955899568995789958899598996089961899628996389964899658996689967899688996989970899718997289973899748997589976899778997889979899808998189982899838998489985899868998789988899898999089991899928999389994899958999689997899988999990000900019000290003900049000590006900079000890009900109001190012900139001490015900169001790018900199002090021900229002390024900259002690027900289002990030900319003290033900349003590036900379003890039900409004190042900439004490045900469004790048900499005090051900529005390054900559005690057900589005990060900619006290063900649006590066900679006890069900709007190072900739007490075900769007790078900799008090081900829008390084900859008690087900889008990090900919009290093900949009590096900979009890099901009010190102901039010490105901069010790108901099011090111901129011390114901159011690117901189011990120901219012290123901249012590126901279012890129901309013190132901339013490135901369013790138901399014090141901429014390144901459014690147901489014990150901519015290153901549015590156901579015890159901609016190162901639016490165901669016790168901699017090171901729017390174901759017690177901789017990180901819018290183901849018590186901879018890189901909019190192901939019490195901969019790198901999020090201902029020390204902059020690207902089020990210902119021290213902149021590216902179021890219902209022190222902239022490225902269022790228902299023090231902329023390234902359023690237902389023990240902419024290243902449024590246902479024890249902509025190252902539025490255902569025790258902599026090261902629026390264902659026690267902689026990270902719027290273902749027590276902779027890279902809028190282902839028490285902869028790288902899029090291902929029390294902959029690297902989029990300903019030290303903049030590306903079030890309903109031190312903139031490315903169031790318903199032090321903229032390324903259032690327903289032990330903319033290333903349033590336903379033890339903409034190342903439034490345903469034790348903499035090351903529035390354903559035690357903589035990360903619036290363903649036590366903679036890369903709037190372903739037490375903769037790378903799038090381903829038390384903859038690387903889038990390903919039290393903949039590396903979039890399904009040190402904039040490405904069040790408904099041090411904129041390414904159041690417904189041990420904219042290423904249042590426904279042890429904309043190432904339043490435904369043790438904399044090441904429044390444904459044690447904489044990450904519045290453904549045590456904579045890459904609046190462904639046490465904669046790468904699047090471904729047390474904759047690477904789047990480904819048290483904849048590486904879048890489904909049190492904939049490495904969049790498904999050090501905029050390504905059050690507905089050990510905119051290513905149051590516905179051890519905209052190522905239052490525905269052790528905299053090531905329053390534905359053690537905389053990540905419054290543905449054590546905479054890549905509055190552905539055490555905569055790558905599056090561905629056390564905659056690567905689056990570905719057290573905749057590576905779057890579905809058190582905839058490585905869058790588905899059090591905929059390594905959059690597905989059990600906019060290603906049060590606906079060890609906109061190612906139061490615906169061790618906199062090621906229062390624906259062690627906289062990630906319063290633906349063590636906379063890639906409064190642906439064490645906469064790648906499065090651906529065390654906559065690657906589065990660906619066290663906649066590666906679066890669906709067190672906739067490675906769067790678906799068090681906829068390684906859068690687906889068990690906919069290693906949069590696906979069890699907009070190702907039070490705907069070790708907099071090711907129071390714907159071690717907189071990720907219072290723907249072590726907279072890729907309073190732907339073490735907369073790738907399074090741907429074390744907459074690747907489074990750907519075290753907549075590756907579075890759907609076190762907639076490765907669076790768907699077090771907729077390774907759077690777907789077990780907819078290783907849078590786907879078890789907909079190792907939079490795907969079790798907999080090801908029080390804908059080690807908089080990810908119081290813908149081590816908179081890819908209082190822908239082490825908269082790828908299083090831908329083390834908359083690837908389083990840908419084290843908449084590846908479084890849908509085190852908539085490855908569085790858908599086090861908629086390864908659086690867908689086990870908719087290873908749087590876908779087890879908809088190882908839088490885908869088790888908899089090891908929089390894908959089690897908989089990900909019090290903909049090590906909079090890909909109091190912909139091490915909169091790918909199092090921909229092390924909259092690927909289092990930909319093290933909349093590936909379093890939909409094190942909439094490945909469094790948909499095090951909529095390954909559095690957909589095990960909619096290963909649096590966909679096890969909709097190972909739097490975909769097790978909799098090981909829098390984909859098690987909889098990990909919099290993909949099590996909979099890999910009100191002910039100491005910069100791008910099101091011910129101391014910159101691017910189101991020910219102291023910249102591026910279102891029910309103191032910339103491035910369103791038910399104091041910429104391044910459104691047910489104991050910519105291053910549105591056910579105891059910609106191062910639106491065910669106791068910699107091071910729107391074910759107691077910789107991080910819108291083910849108591086910879108891089910909109191092910939109491095910969109791098910999110091101911029110391104911059110691107911089110991110911119111291113911149111591116911179111891119911209112191122911239112491125911269112791128911299113091131911329113391134911359113691137911389113991140911419114291143911449114591146911479114891149911509115191152911539115491155911569115791158911599116091161911629116391164911659116691167911689116991170911719117291173911749117591176911779117891179911809118191182911839118491185911869118791188911899119091191911929119391194911959119691197911989119991200912019120291203912049120591206912079120891209912109121191212912139121491215912169121791218912199122091221912229122391224912259122691227912289122991230912319123291233912349123591236912379123891239912409124191242912439124491245912469124791248912499125091251912529125391254912559125691257912589125991260912619126291263912649126591266912679126891269912709127191272912739127491275912769127791278912799128091281912829128391284912859128691287912889128991290912919129291293912949129591296912979129891299913009130191302913039130491305913069130791308913099131091311913129131391314913159131691317913189131991320913219132291323913249132591326913279132891329913309133191332913339133491335913369133791338913399134091341913429134391344913459134691347913489134991350913519135291353913549135591356913579135891359913609136191362913639136491365913669136791368913699137091371913729137391374913759137691377913789137991380913819138291383913849138591386913879138891389913909139191392913939139491395913969139791398913999140091401914029140391404914059140691407914089140991410914119141291413914149141591416914179141891419914209142191422914239142491425914269142791428914299143091431914329143391434914359143691437914389143991440914419144291443914449144591446914479144891449914509145191452914539145491455914569145791458914599146091461914629146391464914659146691467914689146991470914719147291473914749147591476914779147891479914809148191482914839148491485914869148791488914899149091491914929149391494914959149691497914989149991500915019150291503915049150591506915079150891509915109151191512915139151491515915169151791518915199152091521915229152391524915259152691527915289152991530915319153291533915349153591536915379153891539915409154191542915439154491545915469154791548915499155091551915529155391554915559155691557915589155991560915619156291563915649156591566915679156891569915709157191572915739157491575915769157791578915799158091581915829158391584915859158691587915889158991590915919159291593915949159591596915979159891599916009160191602916039160491605916069160791608916099161091611916129161391614916159161691617916189161991620916219162291623916249162591626916279162891629916309163191632916339163491635916369163791638916399164091641916429164391644916459164691647916489164991650916519165291653916549165591656916579165891659916609166191662916639166491665916669166791668916699167091671916729167391674916759167691677916789167991680916819168291683916849168591686916879168891689916909169191692916939169491695916969169791698916999170091701917029170391704917059170691707917089170991710917119171291713917149171591716917179171891719917209172191722917239172491725917269172791728917299173091731917329173391734917359173691737917389173991740917419174291743917449174591746917479174891749917509175191752917539175491755917569175791758917599176091761917629176391764917659176691767917689176991770917719177291773917749177591776917779177891779917809178191782917839178491785917869178791788917899179091791917929179391794917959179691797917989179991800918019180291803918049180591806918079180891809918109181191812918139181491815918169181791818918199182091821918229182391824918259182691827918289182991830918319183291833918349183591836918379183891839918409184191842918439184491845918469184791848918499185091851918529185391854918559185691857918589185991860918619186291863918649186591866918679186891869918709187191872918739187491875918769187791878918799188091881918829188391884918859188691887918889188991890918919189291893918949189591896918979189891899919009190191902919039190491905919069190791908919099191091911919129191391914919159191691917919189191991920919219192291923919249192591926919279192891929919309193191932919339193491935919369193791938919399194091941919429194391944919459194691947919489194991950919519195291953919549195591956919579195891959919609196191962919639196491965919669196791968919699197091971919729197391974919759197691977919789197991980919819198291983919849198591986919879198891989919909199191992919939199491995919969199791998919999200092001920029200392004920059200692007920089200992010920119201292013920149201592016920179201892019920209202192022920239202492025920269202792028920299203092031920329203392034920359203692037920389203992040920419204292043920449204592046920479204892049920509205192052920539205492055920569205792058920599206092061920629206392064920659206692067920689206992070920719207292073920749207592076920779207892079920809208192082920839208492085920869208792088920899209092091920929209392094920959209692097920989209992100921019210292103921049210592106921079210892109921109211192112921139211492115921169211792118921199212092121921229212392124921259212692127921289212992130921319213292133921349213592136921379213892139921409214192142921439214492145921469214792148921499215092151921529215392154921559215692157921589215992160921619216292163921649216592166921679216892169921709217192172921739217492175921769217792178921799218092181921829218392184921859218692187921889218992190921919219292193921949219592196921979219892199922009220192202922039220492205922069220792208922099221092211922129221392214922159221692217922189221992220922219222292223922249222592226922279222892229922309223192232922339223492235922369223792238922399224092241922429224392244922459224692247922489224992250922519225292253922549225592256922579225892259922609226192262922639226492265922669226792268922699227092271922729227392274922759227692277922789227992280922819228292283922849228592286922879228892289922909229192292922939229492295922969229792298922999230092301923029230392304923059230692307923089230992310923119231292313923149231592316923179231892319923209232192322923239232492325923269232792328923299233092331923329233392334923359233692337923389233992340923419234292343923449234592346923479234892349923509235192352923539235492355923569235792358923599236092361923629236392364923659236692367923689236992370923719237292373923749237592376923779237892379923809238192382923839238492385923869238792388923899239092391923929239392394923959239692397923989239992400924019240292403924049240592406924079240892409924109241192412924139241492415924169241792418924199242092421924229242392424924259242692427924289242992430924319243292433924349243592436924379243892439924409244192442924439244492445924469244792448924499245092451924529245392454924559245692457924589245992460924619246292463924649246592466924679246892469924709247192472924739247492475924769247792478924799248092481924829248392484924859248692487924889248992490924919249292493924949249592496924979249892499925009250192502925039250492505925069250792508925099251092511925129251392514925159251692517925189251992520925219252292523925249252592526925279252892529925309253192532925339253492535925369253792538925399254092541925429254392544925459254692547925489254992550925519255292553925549255592556925579255892559925609256192562925639256492565925669256792568925699257092571925729257392574925759257692577925789257992580925819258292583925849258592586925879258892589925909259192592925939259492595925969259792598925999260092601926029260392604926059260692607926089260992610926119261292613926149261592616926179261892619926209262192622926239262492625926269262792628926299263092631926329263392634926359263692637926389263992640926419264292643926449264592646926479264892649926509265192652926539265492655926569265792658926599266092661926629266392664926659266692667926689266992670926719267292673926749267592676926779267892679926809268192682926839268492685926869268792688926899269092691926929269392694926959269692697926989269992700927019270292703927049270592706927079270892709927109271192712927139271492715927169271792718927199272092721927229272392724927259272692727927289272992730927319273292733927349273592736927379273892739927409274192742927439274492745927469274792748927499275092751927529275392754927559275692757927589275992760927619276292763927649276592766927679276892769927709277192772927739277492775927769277792778927799278092781927829278392784927859278692787927889278992790927919279292793927949279592796927979279892799928009280192802928039280492805928069280792808928099281092811928129281392814928159281692817928189281992820928219282292823928249282592826928279282892829928309283192832928339283492835928369283792838928399284092841928429284392844928459284692847928489284992850928519285292853928549285592856928579285892859928609286192862928639286492865928669286792868928699287092871928729287392874928759287692877928789287992880928819288292883928849288592886928879288892889928909289192892928939289492895928969289792898928999290092901929029290392904929059290692907929089290992910929119291292913929149291592916929179291892919929209292192922929239292492925929269292792928929299293092931929329293392934929359293692937929389293992940929419294292943929449294592946929479294892949929509295192952929539295492955929569295792958929599296092961929629296392964929659296692967929689296992970929719297292973929749297592976929779297892979929809298192982929839298492985929869298792988929899299092991929929299392994929959299692997929989299993000930019300293003930049300593006930079300893009930109301193012930139301493015930169301793018930199302093021930229302393024930259302693027930289302993030930319303293033930349303593036930379303893039930409304193042930439304493045930469304793048930499305093051930529305393054930559305693057930589305993060930619306293063930649306593066930679306893069930709307193072930739307493075930769307793078930799308093081930829308393084930859308693087930889308993090930919309293093930949309593096930979309893099931009310193102931039310493105931069310793108931099311093111931129311393114931159311693117931189311993120931219312293123931249312593126931279312893129931309313193132931339313493135931369313793138931399314093141931429314393144931459314693147931489314993150931519315293153931549315593156931579315893159931609316193162931639316493165931669316793168931699317093171931729317393174931759317693177931789317993180931819318293183931849318593186931879318893189931909319193192931939319493195931969319793198931999320093201932029320393204932059320693207932089320993210932119321293213932149321593216932179321893219932209322193222932239322493225932269322793228932299323093231932329323393234932359323693237932389323993240932419324293243932449324593246932479324893249932509325193252932539325493255932569325793258932599326093261932629326393264932659326693267932689326993270932719327293273932749327593276932779327893279932809328193282932839328493285932869328793288932899329093291932929329393294932959329693297932989329993300933019330293303933049330593306933079330893309933109331193312933139331493315933169331793318933199332093321933229332393324933259332693327933289332993330933319333293333933349333593336933379333893339933409334193342933439334493345933469334793348933499335093351933529335393354933559335693357933589335993360933619336293363933649336593366933679336893369933709337193372933739337493375933769337793378933799338093381933829338393384933859338693387933889338993390933919339293393933949339593396933979339893399934009340193402934039340493405934069340793408934099341093411934129341393414934159341693417934189341993420934219342293423934249342593426934279342893429934309343193432934339343493435934369343793438934399344093441934429344393444934459344693447934489344993450934519345293453934549345593456934579345893459934609346193462934639346493465934669346793468934699347093471934729347393474934759347693477934789347993480934819348293483934849348593486934879348893489934909349193492934939349493495934969349793498934999350093501935029350393504935059350693507935089350993510935119351293513935149351593516935179351893519935209352193522935239352493525935269352793528935299353093531935329353393534935359353693537935389353993540935419354293543935449354593546935479354893549935509355193552935539355493555935569355793558935599356093561935629356393564935659356693567935689356993570935719357293573935749357593576935779357893579935809358193582935839358493585935869358793588935899359093591935929359393594935959359693597935989359993600936019360293603936049360593606936079360893609936109361193612936139361493615936169361793618936199362093621936229362393624936259362693627936289362993630936319363293633936349363593636936379363893639936409364193642936439364493645936469364793648936499365093651936529365393654936559365693657936589365993660936619366293663936649366593666936679366893669936709367193672936739367493675936769367793678936799368093681936829368393684936859368693687936889368993690936919369293693936949369593696936979369893699937009370193702937039370493705937069370793708937099371093711937129371393714937159371693717937189371993720937219372293723937249372593726937279372893729937309373193732937339373493735937369373793738937399374093741937429374393744937459374693747937489374993750937519375293753937549375593756937579375893759937609376193762937639376493765937669376793768937699377093771937729377393774937759377693777937789377993780937819378293783937849378593786937879378893789937909379193792937939379493795937969379793798937999380093801938029380393804938059380693807938089380993810938119381293813938149381593816938179381893819938209382193822938239382493825938269382793828938299383093831938329383393834938359383693837938389383993840938419384293843938449384593846938479384893849938509385193852938539385493855938569385793858938599386093861938629386393864938659386693867938689386993870938719387293873938749387593876938779387893879938809388193882938839388493885938869388793888938899389093891938929389393894938959389693897938989389993900939019390293903939049390593906939079390893909939109391193912939139391493915939169391793918939199392093921939229392393924939259392693927939289392993930939319393293933939349393593936939379393893939939409394193942939439394493945939469394793948939499395093951939529395393954939559395693957939589395993960939619396293963939649396593966939679396893969939709397193972939739397493975939769397793978939799398093981939829398393984939859398693987939889398993990939919399293993939949399593996939979399893999940009400194002940039400494005940069400794008940099401094011940129401394014940159401694017940189401994020940219402294023940249402594026940279402894029940309403194032940339403494035940369403794038940399404094041940429404394044940459404694047940489404994050940519405294053940549405594056940579405894059940609406194062940639406494065940669406794068940699407094071940729407394074940759407694077940789407994080940819408294083940849408594086940879408894089940909409194092940939409494095940969409794098940999410094101941029410394104941059410694107941089410994110941119411294113941149411594116941179411894119941209412194122941239412494125941269412794128941299413094131941329413394134941359413694137941389413994140941419414294143941449414594146941479414894149941509415194152941539415494155941569415794158941599416094161941629416394164941659416694167941689416994170941719417294173941749417594176941779417894179941809418194182941839418494185941869418794188941899419094191941929419394194941959419694197941989419994200942019420294203942049420594206942079420894209942109421194212942139421494215942169421794218942199422094221942229422394224942259422694227942289422994230942319423294233942349423594236942379423894239942409424194242942439424494245942469424794248942499425094251942529425394254942559425694257942589425994260942619426294263942649426594266942679426894269942709427194272942739427494275942769427794278942799428094281942829428394284942859428694287942889428994290942919429294293942949429594296942979429894299943009430194302943039430494305943069430794308943099431094311943129431394314943159431694317943189431994320943219432294323943249432594326943279432894329943309433194332943339433494335943369433794338943399434094341943429434394344943459434694347943489434994350943519435294353943549435594356943579435894359943609436194362943639436494365943669436794368943699437094371943729437394374943759437694377943789437994380943819438294383943849438594386943879438894389943909439194392943939439494395943969439794398943999440094401944029440394404944059440694407944089440994410944119441294413944149441594416944179441894419944209442194422944239442494425944269442794428944299443094431944329443394434944359443694437944389443994440944419444294443944449444594446944479444894449944509445194452944539445494455944569445794458944599446094461944629446394464944659446694467944689446994470944719447294473944749447594476944779447894479944809448194482944839448494485944869448794488944899449094491944929449394494944959449694497944989449994500945019450294503945049450594506945079450894509945109451194512945139451494515945169451794518945199452094521945229452394524945259452694527945289452994530945319453294533945349453594536945379453894539945409454194542945439454494545945469454794548945499455094551945529455394554945559455694557945589455994560945619456294563945649456594566945679456894569945709457194572945739457494575945769457794578945799458094581945829458394584945859458694587945889458994590945919459294593945949459594596945979459894599946009460194602946039460494605946069460794608946099461094611946129461394614946159461694617946189461994620946219462294623946249462594626946279462894629946309463194632946339463494635946369463794638946399464094641946429464394644946459464694647946489464994650946519465294653946549465594656946579465894659946609466194662946639466494665946669466794668946699467094671946729467394674946759467694677946789467994680946819468294683946849468594686946879468894689946909469194692946939469494695946969469794698946999470094701947029470394704947059470694707947089470994710947119471294713947149471594716947179471894719947209472194722947239472494725947269472794728947299473094731947329473394734947359473694737947389473994740947419474294743947449474594746947479474894749947509475194752947539475494755947569475794758947599476094761947629476394764947659476694767947689476994770947719477294773947749477594776947779477894779947809478194782947839478494785947869478794788947899479094791947929479394794947959479694797947989479994800948019480294803948049480594806948079480894809948109481194812948139481494815948169481794818948199482094821948229482394824948259482694827948289482994830948319483294833948349483594836948379483894839948409484194842948439484494845948469484794848948499485094851948529485394854948559485694857948589485994860948619486294863948649486594866948679486894869948709487194872948739487494875948769487794878948799488094881948829488394884948859488694887948889488994890948919489294893948949489594896948979489894899949009490194902949039490494905949069490794908949099491094911949129491394914949159491694917949189491994920949219492294923949249492594926949279492894929949309493194932949339493494935949369493794938949399494094941949429494394944949459494694947949489494994950949519495294953949549495594956949579495894959949609496194962949639496494965949669496794968949699497094971949729497394974949759497694977949789497994980949819498294983949849498594986949879498894989949909499194992949939499494995949969499794998949999500095001950029500395004950059500695007950089500995010950119501295013950149501595016950179501895019950209502195022950239502495025950269502795028950299503095031950329503395034950359503695037950389503995040950419504295043950449504595046950479504895049950509505195052950539505495055950569505795058950599506095061950629506395064950659506695067950689506995070950719507295073950749507595076950779507895079950809508195082950839508495085950869508795088950899509095091950929509395094950959509695097950989509995100951019510295103951049510595106951079510895109951109511195112951139511495115951169511795118951199512095121951229512395124951259512695127951289512995130951319513295133951349513595136951379513895139951409514195142951439514495145951469514795148951499515095151951529515395154951559515695157951589515995160951619516295163951649516595166951679516895169951709517195172951739517495175951769517795178951799518095181951829518395184951859518695187951889518995190951919519295193951949519595196951979519895199952009520195202952039520495205952069520795208952099521095211952129521395214952159521695217952189521995220952219522295223952249522595226952279522895229952309523195232952339523495235952369523795238952399524095241952429524395244952459524695247952489524995250952519525295253952549525595256952579525895259952609526195262952639526495265952669526795268952699527095271952729527395274952759527695277952789527995280952819528295283952849528595286952879528895289952909529195292952939529495295952969529795298952999530095301953029530395304953059530695307953089530995310953119531295313953149531595316953179531895319953209532195322953239532495325953269532795328953299533095331953329533395334953359533695337953389533995340953419534295343953449534595346953479534895349953509535195352953539535495355953569535795358953599536095361953629536395364953659536695367953689536995370953719537295373953749537595376953779537895379953809538195382953839538495385953869538795388953899539095391953929539395394953959539695397953989539995400954019540295403954049540595406954079540895409954109541195412954139541495415954169541795418954199542095421954229542395424954259542695427954289542995430954319543295433954349543595436954379543895439954409544195442954439544495445954469544795448954499545095451954529545395454954559545695457954589545995460954619546295463954649546595466954679546895469954709547195472954739547495475954769547795478954799548095481954829548395484954859548695487954889548995490954919549295493954949549595496954979549895499955009550195502955039550495505955069550795508955099551095511955129551395514955159551695517955189551995520955219552295523955249552595526955279552895529955309553195532955339553495535955369553795538955399554095541955429554395544955459554695547955489554995550955519555295553955549555595556955579555895559955609556195562955639556495565955669556795568955699557095571955729557395574955759557695577955789557995580955819558295583955849558595586955879558895589955909559195592955939559495595955969559795598955999560095601956029560395604956059560695607956089560995610956119561295613956149561595616956179561895619956209562195622956239562495625956269562795628956299563095631956329563395634956359563695637956389563995640956419564295643956449564595646956479564895649956509565195652956539565495655956569565795658956599566095661956629566395664956659566695667956689566995670956719567295673956749567595676956779567895679956809568195682956839568495685956869568795688956899569095691956929569395694956959569695697956989569995700957019570295703957049570595706957079570895709957109571195712957139571495715957169571795718957199572095721957229572395724957259572695727957289572995730957319573295733957349573595736957379573895739957409574195742957439574495745957469574795748957499575095751957529575395754957559575695757957589575995760957619576295763957649576595766957679576895769957709577195772957739577495775957769577795778957799578095781957829578395784957859578695787957889578995790957919579295793957949579595796957979579895799958009580195802958039580495805958069580795808958099581095811958129581395814958159581695817958189581995820958219582295823958249582595826958279582895829958309583195832958339583495835958369583795838958399584095841958429584395844958459584695847958489584995850958519585295853958549585595856958579585895859958609586195862958639586495865958669586795868958699587095871958729587395874958759587695877958789587995880958819588295883958849588595886958879588895889958909589195892958939589495895958969589795898958999590095901959029590395904959059590695907959089590995910959119591295913959149591595916959179591895919959209592195922959239592495925959269592795928959299593095931959329593395934959359593695937959389593995940959419594295943959449594595946959479594895949959509595195952959539595495955959569595795958959599596095961959629596395964959659596695967959689596995970959719597295973959749597595976959779597895979959809598195982959839598495985959869598795988959899599095991959929599395994959959599695997959989599996000960019600296003960049600596006960079600896009960109601196012960139601496015960169601796018960199602096021960229602396024960259602696027960289602996030960319603296033960349603596036960379603896039960409604196042960439604496045960469604796048960499605096051960529605396054960559605696057960589605996060960619606296063960649606596066960679606896069960709607196072960739607496075960769607796078960799608096081960829608396084960859608696087960889608996090960919609296093960949609596096960979609896099961009610196102961039610496105961069610796108961099611096111961129611396114961159611696117961189611996120961219612296123961249612596126961279612896129961309613196132961339613496135961369613796138961399614096141961429614396144961459614696147961489614996150961519615296153961549615596156961579615896159961609616196162961639616496165961669616796168961699617096171961729617396174961759617696177961789617996180961819618296183961849618596186961879618896189961909619196192961939619496195961969619796198961999620096201962029620396204962059620696207962089620996210962119621296213962149621596216962179621896219962209622196222962239622496225962269622796228962299623096231962329623396234962359623696237962389623996240962419624296243962449624596246962479624896249962509625196252962539625496255962569625796258962599626096261962629626396264962659626696267962689626996270962719627296273962749627596276962779627896279962809628196282962839628496285962869628796288962899629096291962929629396294962959629696297962989629996300963019630296303963049630596306963079630896309963109631196312963139631496315963169631796318963199632096321963229632396324963259632696327963289632996330963319633296333963349633596336963379633896339963409634196342963439634496345963469634796348963499635096351963529635396354963559635696357963589635996360963619636296363963649636596366963679636896369963709637196372963739637496375963769637796378963799638096381963829638396384963859638696387963889638996390963919639296393963949639596396963979639896399964009640196402964039640496405964069640796408964099641096411964129641396414964159641696417964189641996420964219642296423964249642596426964279642896429964309643196432964339643496435964369643796438964399644096441964429644396444964459644696447964489644996450964519645296453964549645596456964579645896459964609646196462964639646496465964669646796468964699647096471964729647396474964759647696477964789647996480964819648296483964849648596486964879648896489964909649196492964939649496495964969649796498964999650096501965029650396504965059650696507965089650996510965119651296513965149651596516965179651896519965209652196522965239652496525965269652796528965299653096531965329653396534965359653696537965389653996540965419654296543965449654596546965479654896549965509655196552965539655496555965569655796558965599656096561965629656396564965659656696567965689656996570965719657296573965749657596576965779657896579965809658196582965839658496585965869658796588965899659096591965929659396594965959659696597965989659996600966019660296603966049660596606966079660896609966109661196612966139661496615966169661796618966199662096621966229662396624966259662696627966289662996630966319663296633966349663596636966379663896639966409664196642966439664496645966469664796648966499665096651966529665396654966559665696657966589665996660966619666296663966649666596666966679666896669966709667196672966739667496675966769667796678966799668096681966829668396684966859668696687966889668996690966919669296693966949669596696966979669896699967009670196702967039670496705967069670796708967099671096711967129671396714967159671696717967189671996720967219672296723967249672596726967279672896729967309673196732967339673496735967369673796738967399674096741967429674396744967459674696747967489674996750967519675296753967549675596756967579675896759967609676196762967639676496765967669676796768967699677096771967729677396774967759677696777967789677996780967819678296783967849678596786967879678896789967909679196792967939679496795967969679796798967999680096801968029680396804968059680696807968089680996810968119681296813968149681596816968179681896819968209682196822968239682496825968269682796828968299683096831968329683396834968359683696837968389683996840968419684296843968449684596846968479684896849968509685196852968539685496855968569685796858968599686096861968629686396864968659686696867968689686996870968719687296873968749687596876968779687896879968809688196882968839688496885968869688796888968899689096891968929689396894968959689696897968989689996900969019690296903969049690596906969079690896909969109691196912969139691496915969169691796918969199692096921969229692396924969259692696927969289692996930969319693296933969349693596936969379693896939969409694196942969439694496945969469694796948969499695096951969529695396954969559695696957969589695996960969619696296963969649696596966969679696896969969709697196972969739697496975969769697796978969799698096981969829698396984969859698696987969889698996990969919699296993969949699596996969979699896999970009700197002970039700497005970069700797008970099701097011970129701397014970159701697017970189701997020970219702297023970249702597026970279702897029970309703197032970339703497035970369703797038970399704097041970429704397044970459704697047970489704997050970519705297053970549705597056970579705897059970609706197062970639706497065970669706797068970699707097071970729707397074970759707697077970789707997080970819708297083970849708597086970879708897089970909709197092970939709497095970969709797098970999710097101971029710397104971059710697107971089710997110971119711297113971149711597116971179711897119971209712197122971239712497125971269712797128971299713097131971329713397134971359713697137971389713997140971419714297143971449714597146971479714897149971509715197152971539715497155971569715797158971599716097161971629716397164971659716697167971689716997170971719717297173971749717597176971779717897179971809718197182971839718497185971869718797188971899719097191971929719397194971959719697197971989719997200972019720297203972049720597206972079720897209972109721197212972139721497215972169721797218972199722097221972229722397224972259722697227972289722997230972319723297233972349723597236972379723897239972409724197242972439724497245972469724797248972499725097251972529725397254972559725697257972589725997260972619726297263972649726597266972679726897269972709727197272972739727497275972769727797278972799728097281972829728397284972859728697287972889728997290972919729297293972949729597296972979729897299973009730197302973039730497305973069730797308973099731097311973129731397314973159731697317973189731997320973219732297323973249732597326973279732897329973309733197332973339733497335973369733797338973399734097341973429734397344973459734697347973489734997350973519735297353973549735597356973579735897359973609736197362973639736497365973669736797368973699737097371973729737397374973759737697377973789737997380973819738297383973849738597386973879738897389973909739197392973939739497395973969739797398973999740097401974029740397404974059740697407974089740997410974119741297413974149741597416974179741897419974209742197422974239742497425974269742797428974299743097431974329743397434974359743697437974389743997440974419744297443974449744597446974479744897449974509745197452974539745497455974569745797458974599746097461974629746397464974659746697467974689746997470974719747297473974749747597476974779747897479974809748197482974839748497485974869748797488974899749097491974929749397494974959749697497974989749997500975019750297503975049750597506975079750897509975109751197512975139751497515975169751797518975199752097521975229752397524975259752697527975289752997530975319753297533975349753597536975379753897539975409754197542975439754497545975469754797548975499755097551975529755397554975559755697557975589755997560975619756297563975649756597566975679756897569975709757197572975739757497575975769757797578975799758097581975829758397584975859758697587975889758997590975919759297593975949759597596975979759897599976009760197602976039760497605976069760797608976099761097611976129761397614976159761697617976189761997620976219762297623976249762597626976279762897629976309763197632976339763497635976369763797638976399764097641976429764397644976459764697647976489764997650976519765297653976549765597656976579765897659976609766197662976639766497665976669766797668976699767097671976729767397674976759767697677976789767997680976819768297683976849768597686976879768897689976909769197692976939769497695976969769797698976999770097701977029770397704977059770697707977089770997710977119771297713977149771597716977179771897719977209772197722977239772497725977269772797728977299773097731977329773397734977359773697737977389773997740977419774297743977449774597746977479774897749977509775197752977539775497755977569775797758977599776097761977629776397764977659776697767977689776997770977719777297773977749777597776977779777897779977809778197782977839778497785977869778797788977899779097791977929779397794977959779697797977989779997800978019780297803978049780597806978079780897809978109781197812978139781497815978169781797818978199782097821978229782397824978259782697827978289782997830978319783297833978349783597836978379783897839978409784197842978439784497845978469784797848978499785097851978529785397854978559785697857978589785997860978619786297863978649786597866978679786897869978709787197872978739787497875978769787797878978799788097881978829788397884978859788697887978889788997890978919789297893978949789597896978979789897899979009790197902979039790497905979069790797908979099791097911979129791397914979159791697917979189791997920979219792297923979249792597926979279792897929979309793197932979339793497935979369793797938979399794097941979429794397944979459794697947979489794997950979519795297953979549795597956979579795897959979609796197962979639796497965979669796797968979699797097971979729797397974979759797697977979789797997980979819798297983979849798597986979879798897989979909799197992979939799497995979969799797998979999800098001980029800398004980059800698007980089800998010980119801298013980149801598016980179801898019980209802198022980239802498025980269802798028980299803098031980329803398034980359803698037980389803998040980419804298043980449804598046980479804898049980509805198052980539805498055980569805798058980599806098061980629806398064980659806698067980689806998070980719807298073980749807598076980779807898079980809808198082980839808498085980869808798088980899809098091980929809398094980959809698097980989809998100981019810298103981049810598106981079810898109981109811198112981139811498115981169811798118981199812098121981229812398124981259812698127981289812998130981319813298133981349813598136981379813898139981409814198142981439814498145981469814798148981499815098151981529815398154981559815698157981589815998160981619816298163981649816598166981679816898169981709817198172981739817498175981769817798178981799818098181981829818398184981859818698187981889818998190981919819298193981949819598196981979819898199982009820198202982039820498205982069820798208982099821098211982129821398214982159821698217982189821998220982219822298223982249822598226982279822898229982309823198232982339823498235982369823798238982399824098241982429824398244982459824698247982489824998250982519825298253982549825598256982579825898259982609826198262982639826498265982669826798268982699827098271982729827398274982759827698277982789827998280982819828298283982849828598286982879828898289982909829198292982939829498295982969829798298982999830098301983029830398304983059830698307983089830998310983119831298313983149831598316983179831898319983209832198322983239832498325983269832798328983299833098331983329833398334983359833698337983389833998340983419834298343983449834598346983479834898349983509835198352983539835498355983569835798358983599836098361983629836398364983659836698367983689836998370983719837298373983749837598376983779837898379983809838198382983839838498385983869838798388983899839098391983929839398394983959839698397983989839998400984019840298403984049840598406984079840898409984109841198412984139841498415984169841798418984199842098421984229842398424984259842698427984289842998430984319843298433984349843598436984379843898439984409844198442984439844498445984469844798448984499845098451984529845398454984559845698457984589845998460984619846298463984649846598466984679846898469984709847198472984739847498475984769847798478984799848098481984829848398484984859848698487984889848998490984919849298493984949849598496984979849898499985009850198502985039850498505985069850798508985099851098511985129851398514985159851698517985189851998520985219852298523985249852598526985279852898529985309853198532985339853498535985369853798538985399854098541985429854398544985459854698547985489854998550985519855298553985549855598556985579855898559985609856198562985639856498565985669856798568985699857098571985729857398574985759857698577985789857998580985819858298583985849858598586985879858898589985909859198592985939859498595985969859798598985999860098601986029860398604986059860698607986089860998610986119861298613986149861598616986179861898619986209862198622986239862498625986269862798628986299863098631986329863398634986359863698637986389863998640986419864298643986449864598646986479864898649986509865198652986539865498655986569865798658986599866098661986629866398664986659866698667986689866998670986719867298673986749867598676986779867898679986809868198682986839868498685986869868798688986899869098691986929869398694986959869698697986989869998700987019870298703987049870598706987079870898709987109871198712987139871498715987169871798718987199872098721987229872398724987259872698727987289872998730987319873298733987349873598736987379873898739987409874198742987439874498745987469874798748987499875098751987529875398754987559875698757987589875998760987619876298763987649876598766987679876898769987709877198772987739877498775987769877798778987799878098781987829878398784987859878698787987889878998790987919879298793987949879598796987979879898799988009880198802988039880498805988069880798808988099881098811988129881398814988159881698817988189881998820988219882298823988249882598826988279882898829988309883198832988339883498835988369883798838988399884098841988429884398844988459884698847988489884998850988519885298853988549885598856988579885898859988609886198862988639886498865988669886798868988699887098871988729887398874988759887698877988789887998880988819888298883988849888598886988879888898889988909889198892988939889498895988969889798898988999890098901989029890398904989059890698907989089890998910989119891298913989149891598916989179891898919989209892198922989239892498925989269892798928989299893098931989329893398934989359893698937989389893998940989419894298943989449894598946989479894898949989509895198952989539895498955989569895798958989599896098961989629896398964989659896698967989689896998970989719897298973989749897598976989779897898979989809898198982989839898498985989869898798988989899899098991989929899398994989959899698997989989899999000990019900299003990049900599006990079900899009990109901199012990139901499015990169901799018990199902099021990229902399024990259902699027990289902999030990319903299033990349903599036990379903899039990409904199042990439904499045990469904799048990499905099051990529905399054990559905699057990589905999060990619906299063990649906599066990679906899069990709907199072990739907499075990769907799078990799908099081990829908399084990859908699087990889908999090990919909299093990949909599096990979909899099991009910199102991039910499105991069910799108991099911099111991129911399114991159911699117991189911999120991219912299123991249912599126991279912899129991309913199132991339913499135991369913799138991399914099141991429914399144991459914699147991489914999150991519915299153991549915599156991579915899159991609916199162991639916499165991669916799168991699917099171991729917399174991759917699177991789917999180991819918299183991849918599186991879918899189991909919199192991939919499195991969919799198991999920099201992029920399204992059920699207992089920999210992119921299213992149921599216992179921899219992209922199222992239922499225992269922799228992299923099231992329923399234992359923699237992389923999240992419924299243992449924599246992479924899249992509925199252992539925499255992569925799258992599926099261992629926399264992659926699267992689926999270992719927299273992749927599276992779927899279992809928199282992839928499285992869928799288992899929099291992929929399294992959929699297992989929999300993019930299303993049930599306993079930899309993109931199312993139931499315993169931799318993199932099321993229932399324993259932699327993289932999330993319933299333993349933599336993379933899339993409934199342993439934499345993469934799348993499935099351993529935399354993559935699357993589935999360993619936299363993649936599366993679936899369993709937199372993739937499375993769937799378993799938099381993829938399384993859938699387993889938999390993919939299393993949939599396993979939899399994009940199402994039940499405994069940799408994099941099411994129941399414994159941699417
  1. <?php
  2. error_reporting(1803);
  3. if (function_exists('mb_internal_encoding')) {
  4. mb_internal_encoding('ASCII');
  5. }
  6. if (!class_exists('PHP_Archive')) {/**
  7. * PHP_Archive Class (implements .phar)
  8. *
  9. * @package PHP_Archive
  10. * @category PHP
  11. */
  12. /**
  13. * PHP_Archive Class (implements .phar)
  14. *
  15. * PHAR files a singular archive from which an entire application can run.
  16. * To use it, simply package it using {@see PHP_Archive_Creator} and use phar://
  17. * URIs to your includes. i.e. require_once 'phar://config.php' will include config.php
  18. * from the root of the PHAR file.
  19. *
  20. * Gz code borrowed from the excellent File_Archive package by Vincent Lascaux.
  21. *
  22. * @copyright Copyright David Shafik and Synaptic Media 2004. All rights reserved.
  23. * @author Davey Shafik <davey@synapticmedia.net>
  24. * @author Greg Beaver <cellog@php.net>
  25. * @link http://www.synapticmedia.net Synaptic Media
  26. * @version Id$
  27. * @package PHP_Archive
  28. * @category PHP
  29. */
  30. class PHP_Archive
  31. {
  32. const GZ = 0x00001000;
  33. const BZ2 = 0x00002000;
  34. const SIG = 0x00010000;
  35. const SHA1 = 0x0002;
  36. const MD5 = 0x0001;
  37. const SHA256 = 0x0003;
  38. const SHA512 = 0x0004;
  39. const OPENSSL = 0x0010;
  40. /**
  41. * Whether this archive is compressed with zlib
  42. *
  43. * @var bool
  44. */
  45. private $_compressed;
  46. /**
  47. * @var string Real path to the .phar archive
  48. */
  49. private $_archiveName = null;
  50. /**
  51. * Current file name in the phar
  52. * @var string
  53. */
  54. protected $currentFilename = null;
  55. /**
  56. * Length of current file in the phar
  57. * @var string
  58. */
  59. protected $internalFileLength = null;
  60. /**
  61. * true if the current file is an empty directory
  62. * @var string
  63. */
  64. protected $isDir = false;
  65. /**
  66. * Current file statistics (size, creation date, etc.)
  67. * @var string
  68. */
  69. protected $currentStat = null;
  70. /**
  71. * @var resource|null Pointer to open .phar
  72. */
  73. protected $fp = null;
  74. /**
  75. * @var int Current Position of the pointer
  76. */
  77. protected $position = 0;
  78. /**
  79. * Map actual realpath of phars to meta-data about the phar
  80. *
  81. * Data is indexed by the alias that is used by internal files. In other
  82. * words, if a file is included via:
  83. * <code>
  84. * require_once 'phar://PEAR.phar/PEAR/Installer.php';
  85. * </code>
  86. * then the alias is "PEAR.phar"
  87. *
  88. * Information stored is a boolean indicating whether this .phar is compressed
  89. * with zlib, another for bzip2, phar-specific meta-data, and
  90. * the precise offset of internal files
  91. * within the .phar, used with the {@link $_manifest} to load actual file contents
  92. * @var array
  93. */
  94. private static $_pharMapping = array();
  95. /**
  96. * Map real file paths to alias used
  97. *
  98. * @var array
  99. */
  100. private static $_pharFiles = array();
  101. /**
  102. * File listing for the .phar
  103. *
  104. * The manifest is indexed per phar.
  105. *
  106. * Files within the .phar are indexed by their relative path within the
  107. * .phar. Each file has this information in its internal array
  108. *
  109. * - 0 = uncompressed file size
  110. * - 1 = timestamp of when file was added to phar
  111. * - 2 = offset of file within phar relative to internal file's start
  112. * - 3 = compressed file size (actual size in the phar)
  113. * @var array
  114. */
  115. private static $_manifest = array();
  116. /**
  117. * Absolute offset of internal files within the .phar, indexed by absolute
  118. * path to the .phar
  119. *
  120. * @var array
  121. */
  122. private static $_fileStart = array();
  123. /**
  124. * file name of the phar
  125. *
  126. * @var string
  127. */
  128. private $_basename;
  129. /**
  130. * Default MIME types used for the web front controller
  131. *
  132. * @var array
  133. */
  134. public static $defaultmimes = array(
  135. 'aif' => 'audio/x-aiff',
  136. 'aiff' => 'audio/x-aiff',
  137. 'arc' => 'application/octet-stream',
  138. 'arj' => 'application/octet-stream',
  139. 'art' => 'image/x-jg',
  140. 'asf' => 'video/x-ms-asf',
  141. 'asx' => 'video/x-ms-asf',
  142. 'avi' => 'video/avi',
  143. 'bin' => 'application/octet-stream',
  144. 'bm' => 'image/bmp',
  145. 'bmp' => 'image/bmp',
  146. 'bz2' => 'application/x-bzip2',
  147. 'css' => 'text/css',
  148. 'doc' => 'application/msword',
  149. 'dot' => 'application/msword',
  150. 'dv' => 'video/x-dv',
  151. 'dvi' => 'application/x-dvi',
  152. 'eps' => 'application/postscript',
  153. 'exe' => 'application/octet-stream',
  154. 'gif' => 'image/gif',
  155. 'gz' => 'application/x-gzip',
  156. 'gzip' => 'application/x-gzip',
  157. 'htm' => 'text/html',
  158. 'html' => 'text/html',
  159. 'ico' => 'image/x-icon',
  160. 'jpe' => 'image/jpeg',
  161. 'jpg' => 'image/jpeg',
  162. 'jpeg' => 'image/jpeg',
  163. 'js' => 'application/x-javascript',
  164. 'log' => 'text/plain',
  165. 'mid' => 'audio/x-midi',
  166. 'mov' => 'video/quicktime',
  167. 'mp2' => 'audio/mpeg',
  168. 'mp3' => 'audio/mpeg3',
  169. 'mpg' => 'audio/mpeg',
  170. 'pdf' => 'aplication/pdf',
  171. 'png' => 'image/png',
  172. 'rtf' => 'application/rtf',
  173. 'tif' => 'image/tiff',
  174. 'tiff' => 'image/tiff',
  175. 'txt' => 'text/plain',
  176. 'xml' => 'text/xml',
  177. );
  178. public static $defaultphp = array(
  179. 'php' => true
  180. );
  181. public static $defaultphps = array(
  182. 'phps' => true
  183. );
  184. public static $deny = array('/.+\.inc$/');
  185. public static function viewSource($archive, $file)
  186. {
  187. // security, idea borrowed from PHK
  188. if (!file_exists($archive . '.introspect')) {
  189. header("HTTP/1.0 404 Not Found");
  190. return false;
  191. }
  192. if (self::_fileExists($archive, $_GET['viewsource'])) {
  193. $source = highlight_file('phar://go-pear.phar/' .
  194. $_GET['viewsource'], true);
  195. header('Content-Type: text/html');
  196. header('Content-Length: ' . strlen($source));
  197. echo '<html><head><title>Source of ',
  198. htmlspecialchars($_GET['viewsource']), '</title></head>';
  199. echo '<body><h1>Source of ',
  200. htmlspecialchars($_GET['viewsource']), '</h1>';
  201. if (isset($_GET['introspect'])) {
  202. echo '<a href="', htmlspecialchars($_SERVER['PHP_SELF']),
  203. '?introspect=', urlencode(htmlspecialchars($_GET['introspect'])),
  204. '">Return to ', htmlspecialchars($_GET['introspect']), '</a><br />';
  205. }
  206. echo $source;
  207. return false;
  208. } else {
  209. header("HTTP/1.0 404 Not Found");
  210. return false;
  211. }
  212. }
  213. public static function introspect($archive, $dir)
  214. {
  215. // security, idea borrowed from PHK
  216. if (!file_exists($archive . '.introspect')) {
  217. header("HTTP/1.0 404 Not Found");
  218. return false;
  219. }
  220. if (!$dir) {
  221. $dir = '/';
  222. }
  223. $dir = self::processFile($dir);
  224. if ($dir[0] != '/') {
  225. $dir = '/' . $dir;
  226. }
  227. try {
  228. $self = htmlspecialchars($_SERVER['PHP_SELF']);
  229. $iterate = new DirectoryIterator('phar://go-pear.phar' . $dir);
  230. echo '<html><head><title>Introspect ', htmlspecialchars($dir),
  231. '</title></head><body><h1>Introspect ', htmlspecialchars($dir),
  232. '</h1><ul>';
  233. if ($dir != '/') {
  234. echo '<li><a href="', $self, '?introspect=',
  235. htmlspecialchars(dirname($dir)), '">..</a></li>';
  236. }
  237. foreach ($iterate as $entry) {
  238. if ($entry->isDot()) continue;
  239. $name = self::processFile($entry->getPathname());
  240. $name = str_replace('phar://go-pear.phar/', '', $name);
  241. if ($entry->isDir()) {
  242. echo '<li><a href="', $self, '?introspect=',
  243. urlencode(htmlspecialchars($name)),
  244. '">',
  245. htmlspecialchars($entry->getFilename()), '/</a> [directory]</li>';
  246. } else {
  247. echo '<li><a href="', $self, '?introspect=',
  248. urlencode(htmlspecialchars($dir)), '&viewsource=',
  249. urlencode(htmlspecialchars($name)),
  250. '">',
  251. htmlspecialchars($entry->getFilename()), '</a></li>';
  252. }
  253. }
  254. return false;
  255. } catch (Exception $e) {
  256. echo '<html><head><title>Directory not found: ',
  257. htmlspecialchars($dir), '</title></head>',
  258. '<body><h1>Directory not found: ', htmlspecialchars($dir), '</h1>',
  259. '<p>Try <a href="', htmlspecialchars($_SERVER['PHP_SELF']), '?introspect=/">',
  260. 'This link</a></p></body></html>';
  261. return false;
  262. }
  263. }
  264. public static function webFrontController($initfile)
  265. {
  266. if (isset($_SERVER) && isset($_SERVER['REQUEST_URI'])) {
  267. $uri = parse_url($_SERVER['REQUEST_URI']);
  268. $archive = realpath($_SERVER['SCRIPT_FILENAME']);
  269. $subpath = str_replace('/' . basename($archive), '', $uri['path']);
  270. if (!$subpath || $subpath == '/') {
  271. if (isset($_GET['viewsource'])) {
  272. return self::viewSource($archive, $_GET['viewsource']);
  273. }
  274. if (isset($_GET['introspect'])) {
  275. return self::introspect($archive, $_GET['introspect']);
  276. }
  277. $subpath = '/' . $initfile;
  278. }
  279. if (!self::_fileExists($archive, substr($subpath, 1))) {
  280. header("HTTP/1.0 404 Not Found");
  281. return false;
  282. }
  283. foreach (self::$deny as $pattern) {
  284. if (preg_match($pattern, $subpath)) {
  285. header("HTTP/1.0 404 Not Found");
  286. return false;
  287. }
  288. }
  289. $inf = pathinfo(basename($subpath));
  290. if (!isset($inf['extension'])) {
  291. header('Content-Type: text/plain');
  292. header('Content-Length: ' .
  293. self::_filesize($archive, substr($subpath, 1)));
  294. readfile('phar://go-pear.phar' . $subpath);
  295. return false;
  296. }
  297. if (isset(self::$defaultphp[$inf['extension']])) {
  298. include 'phar://go-pear.phar' . $subpath;
  299. return false;
  300. }
  301. if (isset(self::$defaultmimes[$inf['extension']])) {
  302. header('Content-Type: ' . self::$defaultmimes[$inf['extension']]);
  303. header('Content-Length: ' .
  304. self::_filesize($archive, substr($subpath, 1)));
  305. readfile('phar://go-pear.phar' . $subpath);
  306. return false;
  307. }
  308. if (isset(self::$defaultphps[$inf['extension']])) {
  309. header('Content-Type: text/html');
  310. $c = highlight_file('phar://go-pear.phar' . $subpath, true);
  311. header('Content-Length: ' . strlen($c));
  312. echo $c;
  313. return false;
  314. }
  315. header('Content-Type: text/plain');
  316. header('Content-Length: ' .
  317. self::_filesize($archive, substr($subpath, 1)));
  318. readfile('phar://go-pear.phar' . $subpath);
  319. }
  320. }
  321. /**
  322. * Detect end of stub
  323. *
  324. * @param string $buffer stub past '__HALT_'.'COMPILER();'
  325. * @return end of stub, prior to length of manifest.
  326. */
  327. private static final function _endOfStubLength($buffer)
  328. {
  329. $pos = 0;
  330. if (!strlen($buffer)) {
  331. return $pos;
  332. }
  333. if (($buffer[0] == ' ' || $buffer[0] == "\n") && @substr($buffer, 1, 2) == '')
  334. {
  335. $pos += 3;
  336. if ($buffer[$pos] == "\r" && $buffer[$pos+1] == "\n") {
  337. $pos += 2;
  338. }
  339. else if ($buffer[$pos] == "\n") {
  340. $pos += 1;
  341. }
  342. }
  343. return $pos;
  344. }
  345. /**
  346. * Allows loading an external Phar archive without include()ing it
  347. *
  348. * @param string $file phar package to load
  349. * @param string $alias alias to use
  350. * @throws Exception
  351. */
  352. public static final function loadPhar($file, $alias = NULL)
  353. {
  354. $file = realpath($file);
  355. if ($file) {
  356. $fp = fopen($file, 'rb');
  357. $buffer = '';
  358. while (!feof($fp)) {
  359. $buffer .= fread($fp, 8192);
  360. // don't break phars
  361. if ($pos = strpos($buffer, '__HALT_COMPI' . 'LER();')) {
  362. $buffer .= fread($fp, 5);
  363. fclose($fp);
  364. $pos += 18;
  365. $pos += self::_endOfStubLength(substr($buffer, $pos));
  366. return self::_mapPhar($file, $pos, $alias);
  367. }
  368. }
  369. fclose($fp);
  370. }
  371. }
  372. /**
  373. * Map a full real file path to an alias used to refer to the .phar
  374. *
  375. * This function can only be called from the initialization of the .phar itself.
  376. * Any attempt to call from outside the .phar or to re-alias the .phar will fail
  377. * as a security measure.
  378. * @param string $alias
  379. * @param int $dataoffset the value of 43882
  380. */
  381. public static final function mapPhar($alias = NULL, $dataoffset = NULL)
  382. {
  383. try {
  384. $trace = debug_backtrace();
  385. $file = $trace[0]['file'];
  386. // this ensures that this is safe
  387. if (!in_array($file, get_included_files())) {
  388. die('SECURITY ERROR: PHP_Archive::mapPhar can only be called from within ' .
  389. 'the phar that initiates it');
  390. }
  391. $file = realpath($file);
  392. if (!isset($dataoffset)) {
  393. $dataoffset = constant('__COMPILER_HALT_OFFSET'.'__');
  394. $fp = fopen($file, 'rb');
  395. fseek($fp, $dataoffset, SEEK_SET);
  396. $dataoffset = $dataoffset + self::_endOfStubLength(fread($fp, 5));
  397. fclose($fp);
  398. }
  399. self::_mapPhar($file, $dataoffset);
  400. } catch (Exception $e) {
  401. die($e->getMessage());
  402. }
  403. }
  404. /**
  405. * Sub-function, allows recovery from errors
  406. *
  407. * @param unknown_type $file
  408. * @param unknown_type $dataoffset
  409. */
  410. private static function _mapPhar($file, $dataoffset, $alias = NULL)
  411. {
  412. $file = realpath($file);
  413. if (isset(self::$_manifest[$file])) {
  414. return;
  415. }
  416. if (!is_array(self::$_pharMapping)) {
  417. self::$_pharMapping = array();
  418. }
  419. $fp = fopen($file, 'rb');
  420. // seek to __HALT_COMPILER_OFFSET__
  421. fseek($fp, $dataoffset);
  422. $manifest_length = unpack('Vlen', fread($fp, 4));
  423. $manifest = '';
  424. $last = '1';
  425. while (strlen($last) && strlen($manifest) < $manifest_length['len']) {
  426. $read = 8192;
  427. if ($manifest_length['len'] - strlen($manifest) < 8192) {
  428. $read = $manifest_length['len'] - strlen($manifest);
  429. }
  430. $last = fread($fp, $read);
  431. $manifest .= $last;
  432. }
  433. if (strlen($manifest) < $manifest_length['len']) {
  434. throw new Exception('ERROR: manifest length read was "' .
  435. strlen($manifest) .'" should be "' .
  436. $manifest_length['len'] . '"');
  437. }
  438. $info = self::_unserializeManifest($manifest);
  439. if ($info['alias']) {
  440. $alias = $info['alias'];
  441. $explicit = true;
  442. } else {
  443. if (!isset($alias)) {
  444. $alias = $file;
  445. }
  446. $explicit = false;
  447. }
  448. self::$_manifest[$file] = $info['manifest'];
  449. $compressed = $info['compressed'];
  450. self::$_fileStart[$file] = ftell($fp);
  451. fclose($fp);
  452. if ($compressed & 0x00001000) {
  453. if (!function_exists('gzinflate')) {
  454. throw new Exception('Error: zlib extension is not enabled - gzinflate() function needed' .
  455. ' for compressed .phars');
  456. }
  457. }
  458. if ($compressed & 0x00002000) {
  459. if (!function_exists('bzdecompress')) {
  460. throw new Exception('Error: bzip2 extension is not enabled - bzdecompress() function needed' .
  461. ' for compressed .phars');
  462. }
  463. }
  464. if (isset(self::$_pharMapping[$alias])) {
  465. throw new Exception('ERROR: PHP_Archive::mapPhar has already been called for alias "' .
  466. $alias . '" cannot re-alias to "' . $file . '"');
  467. }
  468. self::$_pharMapping[$alias] = array($file, $compressed, $dataoffset, $explicit,
  469. $info['metadata']);
  470. self::$_pharFiles[$file] = $alias;
  471. }
  472. /**
  473. * extract the manifest into an internal array
  474. *
  475. * @param string $manifest
  476. * @return false|array
  477. */
  478. private static function _unserializeManifest($manifest)
  479. {
  480. // retrieve the number of files in the manifest
  481. $info = unpack('V', substr($manifest, 0, 4));
  482. $apiver = substr($manifest, 4, 2);
  483. $apiver = bin2hex($apiver);
  484. $apiver_dots = hexdec($apiver[0]) . '.' . hexdec($apiver[1]) . '.' . hexdec($apiver[2]);
  485. $majorcompat = hexdec($apiver[0]);
  486. $calcapi = explode('.', self::APIVersion());
  487. if ($calcapi[0] != $majorcompat) {
  488. throw new Exception('Phar is incompatible API version ' . $apiver_dots . ', but ' .
  489. 'PHP_Archive is API version '.self::APIVersion());
  490. }
  491. if ($calcapi[0] === '0') {
  492. if (self::APIVersion() != $apiver_dots) {
  493. throw new Exception('Phar is API version ' . $apiver_dots .
  494. ', but PHP_Archive is API version '.self::APIVersion(), E_USER_ERROR);
  495. }
  496. }
  497. $flags = unpack('V', substr($manifest, 6, 4));
  498. $ret = array('compressed' => $flags[1] & 0x00003000);
  499. // signature is not verified by default in PHP_Archive, phar is better
  500. $ret['hassignature'] = $flags[1] & 0x00010000;
  501. $aliaslen = unpack('V', substr($manifest, 10, 4));
  502. if ($aliaslen) {
  503. $ret['alias'] = substr($manifest, 14, $aliaslen[1]);
  504. } else {
  505. $ret['alias'] = false;
  506. }
  507. $manifest = substr($manifest, 14 + $aliaslen[1]);
  508. $metadatalen = unpack('V', substr($manifest, 0, 4));
  509. if ($metadatalen[1]) {
  510. $ret['metadata'] = unserialize(substr($manifest, 4, $metadatalen[1]));
  511. $manifest = substr($manifest, 4 + $metadatalen[1]);
  512. } else {
  513. $ret['metadata'] = null;
  514. $manifest = substr($manifest, 4);
  515. }
  516. $offset = 0;
  517. $start = 0;
  518. for ($i = 0; $i < $info[1]; $i++) {
  519. // length of the file name
  520. $len = unpack('V', substr($manifest, $start, 4));
  521. $start += 4;
  522. // file name
  523. $savepath = substr($manifest, $start, $len[1]);
  524. $start += $len[1];
  525. // retrieve manifest data:
  526. // 0 = uncompressed file size
  527. // 1 = timestamp of when file was added to phar
  528. // 2 = compressed filesize
  529. // 3 = crc32
  530. // 4 = flags
  531. // 5 = metadata length
  532. $ret['manifest'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($manifest, $start, 24)));
  533. $ret['manifest'][$savepath][3] = sprintf('%u', $ret['manifest'][$savepath][3]
  534. & 0xffffffff);
  535. if ($ret['manifest'][$savepath][5]) {
  536. $ret['manifest'][$savepath][6] = unserialize(substr($manifest, $start + 24,
  537. $ret['manifest'][$savepath][5]));
  538. } else {
  539. $ret['manifest'][$savepath][6] = null;
  540. }
  541. $ret['manifest'][$savepath][7] = $offset;
  542. $offset += $ret['manifest'][$savepath][2];
  543. $start += 24 + $ret['manifest'][$savepath][5];
  544. }
  545. return $ret;
  546. }
  547. /**
  548. * @param string
  549. */
  550. private static function processFile($path)
  551. {
  552. if ($path == '.') {
  553. return '';
  554. }
  555. $std = str_replace("\\", "/", $path);
  556. while ($std != ($std = preg_replace("/[^\/:?]+\/\.\.\//", "", $std))) ;
  557. $std = str_replace("/./", "", $std);
  558. if (strlen($std) > 1 && $std[0] == '/') {
  559. $std = substr($std, 1);
  560. }
  561. if (strncmp($std, "./", 2) == 0) {
  562. return substr($std, 2);
  563. } else {
  564. return $std;
  565. }
  566. }
  567. /**
  568. * Seek in the master archive to a matching file or directory
  569. * @param string
  570. */
  571. protected function selectFile($path, $allowdirs = true)
  572. {
  573. $std = self::processFile($path);
  574. if (isset(self::$_manifest[$this->_archiveName][$path])) {
  575. if ($path[strlen($path)-1] == '/') {
  576. // directory
  577. if (!$allowdirs) {
  578. return 'Error: "' . $path . '" is a directory in phar "' . $this->_basename . '"';
  579. }
  580. $this->_setCurrentFile($path, true);
  581. } else {
  582. $this->_setCurrentFile($path);
  583. }
  584. return true;
  585. }
  586. if (!$allowdirs) {
  587. return 'Error: "' . $path . '" is not a file in phar "' . $this->_basename . '"';
  588. }
  589. foreach (self::$_manifest[$this->_archiveName] as $file => $info) {
  590. if (empty($std) ||
  591. //$std is a directory
  592. strncmp($std.'/', $path, strlen($std)+1) == 0) {
  593. $this->currentFilename = $this->internalFileLength = $this->currentStat = null;
  594. return true;
  595. }
  596. }
  597. return 'Error: "' . $path . '" not found in phar "' . $this->_basename . '"';
  598. }
  599. private function _setCurrentFile($path, $dir = false)
  600. {
  601. if ($dir) {
  602. $this->currentStat = array(
  603. 2 => 040777, // directory mode, readable by all, writeable by none
  604. 4 => 0, // uid
  605. 5 => 0, // gid
  606. 7 => 0, // size
  607. 9 => self::$_manifest[$this->_archiveName][$path][1], // creation time
  608. );
  609. $this->internalFileLength = 0;
  610. $this->isDir = true;
  611. } else {
  612. $this->currentStat = array(
  613. 2 => 0100444, // file mode, readable by all, writeable by none
  614. 4 => 0, // uid
  615. 5 => 0, // gid
  616. 7 => self::$_manifest[$this->_archiveName][$path][0], // size
  617. 9 => self::$_manifest[$this->_archiveName][$path][1], // creation time
  618. );
  619. $this->internalFileLength = self::$_manifest[$this->_archiveName][$path][2];
  620. $this->isDir = false;
  621. }
  622. $this->currentFilename = $path;
  623. // seek to offset of file header within the .phar
  624. if (is_resource(@$this->fp)) {
  625. fseek($this->fp, self::$_fileStart[$this->_archiveName] + self::$_manifest[$this->_archiveName][$path][7]);
  626. }
  627. }
  628. private static function _fileExists($archive, $path)
  629. {
  630. return isset(self::$_manifest[$archive]) &&
  631. isset(self::$_manifest[$archive][$path]);
  632. }
  633. private static function _filesize($archive, $path)
  634. {
  635. return self::$_manifest[$archive][$path][0];
  636. }
  637. /**
  638. * Seek to a file within the master archive, and extract its contents
  639. * @param string
  640. * @return array|string an array containing an error message string is returned
  641. * upon error, otherwise the file contents are returned
  642. */
  643. public function extractFile($path)
  644. {
  645. $this->fp = @fopen($this->_archiveName, "rb");
  646. if (!$this->fp) {
  647. return array('Error: cannot open phar "' . $this->_archiveName . '"');
  648. }
  649. if (($e = $this->selectFile($path, false)) === true) {
  650. $data = '';
  651. $count = $this->internalFileLength;
  652. while ($count) {
  653. if ($count < 8192) {
  654. $data .= @fread($this->fp, $count);
  655. $count = 0;
  656. } else {
  657. $count -= 8192;
  658. $data .= @fread($this->fp, 8192);
  659. }
  660. }
  661. @fclose($this->fp);
  662. if (self::$_manifest[$this->_archiveName][$path][4] & self::GZ) {
  663. $data = gzinflate($data);
  664. } elseif (self::$_manifest[$this->_archiveName][$path][4] & self::BZ2) {
  665. $data = bzdecompress($data);
  666. }
  667. if (!isset(self::$_manifest[$this->_archiveName][$path]['ok'])) {
  668. if (strlen($data) != $this->currentStat[7]) {
  669. return array("Not valid internal .phar file (size error {$size} != " .
  670. $this->currentStat[7] . ")");
  671. }
  672. if (self::$_manifest[$this->_archiveName][$path][3] != sprintf("%u", crc32($data) & 0xffffffff)) {
  673. return array("Not valid internal .phar file (checksum error)");
  674. }
  675. self::$_manifest[$this->_archiveName][$path]['ok'] = true;
  676. }
  677. return $data;
  678. } else {
  679. @fclose($this->fp);
  680. return array($e);
  681. }
  682. }
  683. /**
  684. * Parse urls like phar:///fullpath/to/my.phar/file.txt
  685. *
  686. * @param string $file
  687. * @return false|array
  688. */
  689. static protected function parseUrl($file)
  690. {
  691. if (substr($file, 0, 7) != 'phar://') {
  692. return false;
  693. }
  694. $file = substr($file, 7);
  695. $ret = array('scheme' => 'phar');
  696. $pos_p = strpos($file, '.phar.php');
  697. $pos_z = strpos($file, '.phar.gz');
  698. $pos_b = strpos($file, '.phar.bz2');
  699. if ($pos_p) {
  700. if ($pos_z) {
  701. return false;
  702. }
  703. $ret['host'] = substr($file, 0, $pos_p + strlen('.phar.php'));
  704. $ret['path'] = substr($file, strlen($ret['host']));
  705. } elseif ($pos_z) {
  706. $ret['host'] = substr($file, 0, $pos_z + strlen('.phar.gz'));
  707. $ret['path'] = substr($file, strlen($ret['host']));
  708. } elseif ($pos_b) {
  709. $ret['host'] = substr($file, 0, $pos_z + strlen('.phar.bz2'));
  710. $ret['path'] = substr($file, strlen($ret['host']));
  711. } elseif (($pos_p = strpos($file, ".phar")) !== false) {
  712. $ret['host'] = substr($file, 0, $pos_p + strlen('.phar'));
  713. $ret['path'] = substr($file, strlen($ret['host']));
  714. } else {
  715. return false;
  716. }
  717. if (!$ret['path']) {
  718. $ret['path'] = '/';
  719. }
  720. return $ret;
  721. }
  722. /**
  723. * Locate the .phar archive in the include_path and detect the file to open within
  724. * the archive.
  725. *
  726. * Possible parameters are phar://pharname.phar/filename_within_phar.ext
  727. * @param string a file within the archive
  728. * @return string the filename within the .phar to retrieve
  729. */
  730. public function initializeStream($file)
  731. {
  732. $file = self::processFile($file);
  733. $info = @parse_url($file);
  734. if (!$info) {
  735. $info = self::parseUrl($file);
  736. }
  737. if (!$info) {
  738. return false;
  739. }
  740. if (!isset($info['host'])) {
  741. // malformed internal file
  742. return false;
  743. }
  744. if (!isset(self::$_pharFiles[$info['host']]) &&
  745. !isset(self::$_pharMapping[$info['host']])) {
  746. try {
  747. self::loadPhar($info['host']);
  748. // use alias from here out
  749. $info['host'] = self::$_pharFiles[$info['host']];
  750. } catch (Exception $e) {
  751. return false;
  752. }
  753. }
  754. if (!isset($info['path'])) {
  755. return false;
  756. } elseif (strlen($info['path']) > 1) {
  757. $info['path'] = substr($info['path'], 1);
  758. }
  759. if (isset(self::$_pharMapping[$info['host']])) {
  760. $this->_basename = $info['host'];
  761. $this->_archiveName = self::$_pharMapping[$info['host']][0];
  762. $this->_compressed = self::$_pharMapping[$info['host']][1];
  763. } elseif (isset(self::$_pharFiles[$info['host']])) {
  764. $this->_archiveName = $info['host'];
  765. $this->_basename = self::$_pharFiles[$info['host']];
  766. $this->_compressed = self::$_pharMapping[$this->_basename][1];
  767. }
  768. $file = $info['path'];
  769. return $file;
  770. }
  771. /**
  772. * Open the requested file - PHP streams API
  773. *
  774. * @param string $file String provided by the Stream wrapper
  775. * @access private
  776. */
  777. public function stream_open($file)
  778. {
  779. return $this->_streamOpen($file);
  780. }
  781. /**
  782. * @param string filename to opne, or directory name
  783. * @param bool if true, a directory will be matched, otherwise only files
  784. * will be matched
  785. * @uses trigger_error()
  786. * @return bool success of opening
  787. * @access private
  788. */
  789. private function _streamOpen($file, $searchForDir = false)
  790. {
  791. $path = $this->initializeStream($file);
  792. if (!$path) {
  793. trigger_error('Error: Unknown phar in "' . $file . '"', E_USER_ERROR);
  794. }
  795. if (is_array($this->file = $this->extractFile($path))) {
  796. trigger_error($this->file[0], E_USER_ERROR);
  797. return false;
  798. }
  799. if ($path != $this->currentFilename) {
  800. if (!$searchForDir) {
  801. trigger_error("Cannot open '$file', is a directory", E_USER_ERROR);
  802. return false;
  803. } else {
  804. $this->file = '';
  805. return true;
  806. }
  807. }
  808. if (!is_null($this->file) && $this->file !== false) {
  809. return true;
  810. } else {
  811. return false;
  812. }
  813. }
  814. /**
  815. * Read the data - PHP streams API
  816. *
  817. * @param int
  818. * @access private
  819. */
  820. public function stream_read($count)
  821. {
  822. $ret = substr($this->file, $this->position, $count);
  823. $this->position += strlen($ret);
  824. return $ret;
  825. }
  826. /**
  827. * Whether we've hit the end of the file - PHP streams API
  828. * @access private
  829. */
  830. function stream_eof()
  831. {
  832. return $this->position >= $this->currentStat[7];
  833. }
  834. /**
  835. * For seeking the stream - PHP streams API
  836. * @param int
  837. * @param SEEK_SET|SEEK_CUR|SEEK_END
  838. * @access private
  839. */
  840. public function stream_seek($pos, $whence)
  841. {
  842. switch ($whence) {
  843. case SEEK_SET:
  844. if ($pos < 0) {
  845. return false;
  846. }
  847. $this->position = $pos;
  848. break;
  849. case SEEK_CUR:
  850. if ($pos + $this->currentStat[7] < 0) {
  851. return false;
  852. }
  853. $this->position += $pos;
  854. break;
  855. case SEEK_END:
  856. if ($pos + $this->currentStat[7] < 0) {
  857. return false;
  858. }
  859. $this->position = $pos + $this->currentStat[7];
  860. break;
  861. default:
  862. return false;
  863. }
  864. return true;
  865. }
  866. /**
  867. * The current position in the stream - PHP streams API
  868. * @access private
  869. */
  870. public function stream_tell()
  871. {
  872. return $this->position;
  873. }
  874. /**
  875. * The result of an fstat call, returns mod time from creation, and file size -
  876. * PHP streams API
  877. * @uses _stream_stat()
  878. * @access private
  879. */
  880. public function stream_stat()
  881. {
  882. return $this->_stream_stat();
  883. }
  884. /**
  885. * Retrieve statistics on a file or directory within the .phar
  886. * @param string file/directory to stat
  887. * @access private
  888. */
  889. public function _stream_stat($file = null)
  890. {
  891. $std = $file ? self::processFile($file) : $this->currentFilename;
  892. if ($file) {
  893. if (isset(self::$_manifest[$this->_archiveName][$file])) {
  894. $this->_setCurrentFile($file);
  895. $isdir = false;
  896. } else {
  897. do {
  898. $isdir = false;
  899. if ($file == '/') {
  900. break;
  901. }
  902. foreach (self::$_manifest[$this->_archiveName] as $path => $info) {
  903. if (strpos($path, $file) === 0) {
  904. if (strlen($path) > strlen($file) &&
  905. $path[strlen($file)] == '/') {
  906. break 2;
  907. }
  908. }
  909. }
  910. // no files exist and no directories match this string
  911. return false;
  912. } while (false);
  913. $isdir = true;
  914. }
  915. } else {
  916. $isdir = false; // open streams must be files
  917. }
  918. $mode = $isdir ? 0040444 : 0100444;
  919. // 040000 = dir, 010000 = file
  920. // everything is readable, nothing is writeable
  921. return array(
  922. 0, 0, $mode, 0, 0, 0, 0, 0, 0, 0, 0, 0, // non-associative indices
  923. 'dev' => 0, 'ino' => 0,
  924. 'mode' => $mode,
  925. 'nlink' => 0, 'uid' => 0, 'gid' => 0, 'rdev' => 0, 'blksize' => 0, 'blocks' => 0,
  926. 'size' => $this->currentStat ? $this->currentStat[7] : 0,
  927. 'atime' => $this->currentStat ? $this->currentStat[9] : 0,
  928. 'mtime' => $this->currentStat ? $this->currentStat[9] : 0,
  929. 'ctime' => $this->currentStat ? $this->currentStat[9] : 0,
  930. );
  931. }
  932. /**
  933. * Stat a closed file or directory - PHP streams API
  934. * @param string
  935. * @param int
  936. * @access private
  937. */
  938. public function url_stat($url, $flags)
  939. {
  940. $path = $this->initializeStream($url);
  941. return $this->_stream_stat($path);
  942. }
  943. /**
  944. * Set a stream option - PHP stream API
  945. * @param int
  946. * @param int
  947. * @param int
  948. * @access private
  949. */
  950. public function stream_set_option($option, $arg1, $arg2)
  951. {
  952. // Simply ignore set options.
  953. return false;
  954. }
  955. /**
  956. * Open a directory in the .phar for reading - PHP streams API
  957. * @param string directory name
  958. * @access private
  959. */
  960. public function dir_opendir($path)
  961. {
  962. $info = @parse_url($path);
  963. if (!$info) {
  964. $info = self::parseUrl($path);
  965. if (!$info) {
  966. trigger_error('Error: "' . $path . '" is a file, and cannot be opened with opendir',
  967. E_USER_ERROR);
  968. return false;
  969. }
  970. }
  971. $path = !empty($info['path']) ?
  972. $info['host'] . $info['path'] : $info['host'] . '/';
  973. $path = $this->initializeStream('phar://' . $path);
  974. if (isset(self::$_manifest[$this->_archiveName][$path])) {
  975. trigger_error('Error: "' . $path . '" is a file, and cannot be opened with opendir',
  976. E_USER_ERROR);
  977. return false;
  978. }
  979. if ($path == false) {
  980. trigger_error('Error: Unknown phar in "' . $file . '"', E_USER_ERROR);
  981. return false;
  982. }
  983. $this->fp = @fopen($this->_archiveName, "rb");
  984. if (!$this->fp) {
  985. trigger_error('Error: cannot open phar "' . $this->_archiveName . '"');
  986. return false;
  987. }
  988. $this->_dirFiles = array();
  989. foreach (self::$_manifest[$this->_archiveName] as $file => $info) {
  990. if ($path == '/') {
  991. if (strpos($file, '/')) {
  992. $a = explode('/', $file);
  993. $this->_dirFiles[array_shift($a)] = true;
  994. } else {
  995. $this->_dirFiles[$file] = true;
  996. }
  997. } elseif (strpos($file, $path) === 0) {
  998. $fname = substr($file, strlen($path) + 1);
  999. if ($fname == '/' || $fname[strlen($fname)-1] == '/') {
  1000. continue; // empty directory
  1001. }
  1002. if (strpos($fname, '/')) {
  1003. // this is a directory
  1004. $a = explode('/', $fname);
  1005. $this->_dirFiles[array_shift($a)] = true;
  1006. } elseif ($file[strlen($path)] == '/') {
  1007. // this is a file
  1008. $this->_dirFiles[$fname] = true;
  1009. }
  1010. }
  1011. }
  1012. @fclose($this->fp);
  1013. if (!count($this->_dirFiles)) {
  1014. return false;
  1015. }
  1016. @uksort($this->_dirFiles, 'strnatcmp');
  1017. return true;
  1018. }
  1019. /**
  1020. * Read the next directory entry - PHP streams API
  1021. * @access private
  1022. */
  1023. public function dir_readdir()
  1024. {
  1025. $ret = key($this->_dirFiles);
  1026. @next($this->_dirFiles);
  1027. if (!$ret) {
  1028. return false;
  1029. }
  1030. return $ret;
  1031. }
  1032. /**
  1033. * Close a directory handle opened with opendir() - PHP streams API
  1034. * @access private
  1035. */
  1036. public function dir_closedir()
  1037. {
  1038. $this->_dirFiles = array();
  1039. return true;
  1040. }
  1041. /**
  1042. * Rewind to the first directory entry - PHP streams API
  1043. * @access private
  1044. */
  1045. public function dir_rewinddir()
  1046. {
  1047. @reset($this->_dirFiles);
  1048. return true;
  1049. }
  1050. /**
  1051. * API version of this class
  1052. * @return string
  1053. */
  1054. public static final function APIVersion()
  1055. {
  1056. return '1.1.0';
  1057. }
  1058. /**
  1059. * Retrieve Phar-specific metadata for a Phar archive
  1060. *
  1061. * @param string $phar full path to Phar archive, or alias
  1062. * @return null|mixed The value that was serialized for the Phar
  1063. * archive's metadata
  1064. * @throws Exception
  1065. */
  1066. public static function getPharMetadata($phar)
  1067. {
  1068. if (isset(self::$_pharFiles[$phar])) {
  1069. $phar = self::$_pharFiles[$phar];
  1070. }
  1071. if (!isset(self::$_pharMapping[$phar])) {
  1072. throw new Exception('Unknown Phar archive: "' . $phar . '"');
  1073. }
  1074. return self::$_pharMapping[$phar][4];
  1075. }
  1076. /**
  1077. * Retrieve File-specific metadata for a Phar archive file
  1078. *
  1079. * @param string $phar full path to Phar archive, or alias
  1080. * @param string $file relative path to file within Phar archive
  1081. * @return null|mixed The value that was serialized for the Phar
  1082. * archive's metadata
  1083. * @throws Exception
  1084. */
  1085. public static function getFileMetadata($phar, $file)
  1086. {
  1087. if (!isset(self::$_pharFiles[$phar])) {
  1088. if (!isset(self::$_pharMapping[$phar])) {
  1089. throw new Exception('Unknown Phar archive: "' . $phar . '"');
  1090. }
  1091. $phar = self::$_pharMapping[$phar][0];
  1092. }
  1093. if (!isset(self::$_manifest[$phar])) {
  1094. throw new Exception('Unknown Phar: "' . $phar . '"');
  1095. }
  1096. $file = self::processFile($file);
  1097. if (!isset(self::$_manifest[$phar][$file])) {
  1098. throw new Exception('Unknown file "' . $file . '" within Phar "'. $phar . '"');
  1099. }
  1100. return self::$_manifest[$phar][$file][6];
  1101. }
  1102. /**
  1103. * @return list of supported signature algorithmns.
  1104. */
  1105. public static function getSupportedSignatures()
  1106. {
  1107. $ret = array('MD5', 'SHA-1');
  1108. if (extension_loaded('hash')) {
  1109. $ret[] = 'SHA-256';
  1110. $ret[] = 'SHA-512';
  1111. }
  1112. if (extension_loaded('openssl')) {
  1113. $ret[] = 'OpenSSL';
  1114. }
  1115. return $ret;
  1116. }
  1117. }}
  1118. if (!class_exists('Phar')) {
  1119. PHP_Archive::mapPhar(null, 43882 );
  1120. } else {
  1121. try {
  1122. Phar::mapPhar();
  1123. } catch (Exception $e) {
  1124. echo $e->getMessage();
  1125. }
  1126. }
  1127. if (class_exists('PHP_Archive') && !in_array('phar', stream_get_wrappers())) {
  1128. stream_wrapper_register('phar', 'PHP_Archive');
  1129. }
  1130. @ini_set('memory_limit', -1);
  1131. if (extension_loaded('phar')) {if (isset($_SERVER) && isset($_SERVER['REQUEST_URI'])) {
  1132. $uri = parse_url($_SERVER['REQUEST_URI']);
  1133. $archive = realpath($_SERVER['SCRIPT_FILENAME']);
  1134. $subpath = str_replace('/' . basename($archive), '', $uri['path']);
  1135. $mimetypes = array (
  1136. 'aif' => 'audio/x-aiff',
  1137. 'aiff' => 'audio/x-aiff',
  1138. 'arc' => 'application/octet-stream',
  1139. 'arj' => 'application/octet-stream',
  1140. 'art' => 'image/x-jg',
  1141. 'asf' => 'video/x-ms-asf',
  1142. 'asx' => 'video/x-ms-asf',
  1143. 'avi' => 'video/avi',
  1144. 'bin' => 'application/octet-stream',
  1145. 'bm' => 'image/bmp',
  1146. 'bmp' => 'image/bmp',
  1147. 'bz2' => 'application/x-bzip2',
  1148. 'css' => 'text/css',
  1149. 'doc' => 'application/msword',
  1150. 'dot' => 'application/msword',
  1151. 'dv' => 'video/x-dv',
  1152. 'dvi' => 'application/x-dvi',
  1153. 'eps' => 'application/postscript',
  1154. 'exe' => 'application/octet-stream',
  1155. 'gif' => 'image/gif',
  1156. 'gz' => 'application/x-gzip',
  1157. 'gzip' => 'application/x-gzip',
  1158. 'htm' => 'text/html',
  1159. 'html' => 'text/html',
  1160. 'ico' => 'image/x-icon',
  1161. 'jpe' => 'image/jpeg',
  1162. 'jpg' => 'image/jpeg',
  1163. 'jpeg' => 'image/jpeg',
  1164. 'js' => 'application/x-javascript',
  1165. 'log' => 'text/plain',
  1166. 'mid' => 'audio/x-midi',
  1167. 'mov' => 'video/quicktime',
  1168. 'mp2' => 'audio/mpeg',
  1169. 'mp3' => 'audio/mpeg3',
  1170. 'mpg' => 'audio/mpeg',
  1171. 'pdf' => 'aplication/pdf',
  1172. 'png' => 'image/png',
  1173. 'rtf' => 'application/rtf',
  1174. 'tif' => 'image/tiff',
  1175. 'tiff' => 'image/tiff',
  1176. 'txt' => 'text/plain',
  1177. 'xml' => 'text/xml',
  1178. );
  1179. $phpfiles = array (
  1180. 'php' => true,
  1181. );
  1182. $phpsfiles = array (
  1183. 'phps' => true,
  1184. );
  1185. $deny = array (
  1186. 0 => '/.+\\.inc$/',
  1187. );
  1188. $subpath = str_replace('/' . basename($archive), '', $uri['path']);
  1189. if (!$subpath || $subpath == '/') {
  1190. $subpath = '/PEAR.php';
  1191. }
  1192. if ($subpath[0] != '/') {
  1193. $subpath = '/' . $subpath;
  1194. }
  1195. if (!@file_exists('phar://' . $archive . $subpath)) {
  1196. header("HTTP/1.0 404 Not Found");
  1197. exit;
  1198. }
  1199. foreach ($deny as $pattern) {
  1200. if (preg_match($pattern, $subpath)) {
  1201. header("HTTP/1.0 404 Not Found");
  1202. exit;
  1203. }
  1204. }
  1205. $inf = pathinfo(basename($subpath));
  1206. if (!isset($inf['extension'])) {
  1207. header('Content-Type: text/plain');
  1208. header('Content-Length: ' . filesize('phar://' . $archive . $subpath));
  1209. readfile('phar://' . $archive . $subpath);
  1210. exit;
  1211. }
  1212. if (isset($phpfiles[$inf['extension']])) {
  1213. include 'phar://' . $archive . '/' . $subpath;
  1214. exit;
  1215. }
  1216. if (isset($mimetypes[$inf['extension']])) {
  1217. header('Content-Type: ' . $mimetypes[$inf['extension']]);
  1218. header('Content-Length: ' . filesize('phar://' . $archive . $subpath));
  1219. readfile('phar://' . $archive . $subpath);
  1220. exit;
  1221. }
  1222. if (isset($phpsfiles[$inf['extension']])) {
  1223. header('Content-Type: text/html');
  1224. $c = highlight_file('phar://' . $archive . $subpath, true);
  1225. header('Content-Length: ' . strlen($c));
  1226. echo $c;
  1227. exit;
  1228. }
  1229. header('Content-Type: text/plain');
  1230. header('Content-Length: ' . filesize('phar://' . $archive . '/' . $subpath));
  1231. readfile('phar://' . $archive . '/' . $subpath);
  1232. exit;
  1233. }} else {if (!empty($_SERVER['REQUEST_URI'])) {PHP_Archive::webFrontController('PEAR.php');exit;}}
  1234. require_once 'phar://go-pear.phar/index.php';
  1235. __HALT_COMPILER();h��F������� ���go-pear.phar�������Archive/Tar.php?Y�Z¥Cg?Y�È™êóm���������Console/Getopt.php\5��Z¥Cg\5��ŠÍ;@m������ ���index.php‹���Z¥Cg‹���A�#m������ ���OS/Guess.php-��Z¥Cg-��Æ–%8m���������PEAR.php”���Z¥Cg”���ü'7,m���������PEAR/ChannelFile.phpKÇ��Z¥CgKÇ�� ÷Ñm���������PEAR/ChannelFile/Parser.phpÞ��Z¥CgÞ��j¸­¬m���������PEAR/Command.phpÖ0��Z¥CgÖ0��qªÀYm���������PEAR/Command/Common.php/ ��Z¥Cg/ ��@”+[m���������PEAR/Command/Install.phpŸÇ��Z¥CgŸÇ��‡çŽm���������PEAR/Command/Install.xmló!��Z¥Cgó!��jÇ¢m���������PEAR/Common.php\h��Z¥Cg\h�� Klm���������PEAR/Config.phpÌ�Z¥CgÌ�€×m���������PEAR/Dependency2.php‹Å��Z¥Cg‹Å��ð:¸m���������PEAR/DependencyDB.php3_��Z¥Cg3_��òÈb�m���������PEAR/Downloader.phpP�Z¥CgP�í½Žm���������PEAR/Downloader/Package.php^*�Z¥Cg^*�h�%hm���������PEAR/ErrorStack.php%„��Z¥Cg%„��Ï¥ em���������PEAR/Frontend.php��Z¥Cg���ªÁm���������PEAR/Frontend/CLI.phpfd��Z¥Cgfd��ë!ÒJm������+���PEAR/go-pear-tarballs/Archive_Tar-1.5.0.tar��Z¥Cg��àwÄ;m������.���PEAR/go-pear-tarballs/Console_Getopt-1.4.3.tar�z��Z¥Cg�z��ÀUÚ/m������&���PEAR/go-pear-tarballs/PEAR-1.10.16.tar�P�Z¥Cg�P�Z˜Rm������0���PEAR/go-pear-tarballs/Structures_Graph-1.2.0.tar�8�Z¥Cg�8�(ò¯ßm������(���PEAR/go-pear-tarballs/XML_Util-1.4.5.tar�@�Z¥Cg�@�*B¡km���������PEAR/Installer.phpõ�Z¥Cgõ�w±Tm���������PEAR/Installer/Role.php��Z¥Cg��’—ˆm���������PEAR/Installer/Role/Common.phpG��Z¥CgG��?ëËÅm���������PEAR/Installer/Role/Data.php��Z¥Cg�� Œ‘
  1236. m���������PEAR/Installer/Role/Data.xml’��Z¥Cg’��f�szm���������PEAR/Installer/Role/Doc.php ��Z¥Cg ��¦Ñm���������PEAR/Installer/Role/Doc.xml‘��Z¥Cg‘��h&P*m���������PEAR/Installer/Role/Php.php ��Z¥Cg ��Ò?mm���������PEAR/Installer/Role/Php.xml­��Z¥Cg­��z�q�m���������PEAR/Installer/Role/Script.php��Z¥Cg��ÈÅ[Îm���������PEAR/Installer/Role/Script.xml°��Z¥Cg°��@v§Ðm���������PEAR/Installer/Role/Test.php��Z¥Cg��Y—ö„m���������PEAR/Installer/Role/Test.xml’��Z¥Cg’��B] m���������PEAR/PackageFile.phpc>��Z¥Cgc>��Žåÿ`m������!���PEAR/PackageFile/Generator/v1.phpÅ��Z¥CgÅ��ô9ìtm������!���PEAR/PackageFile/Generator/v2.php‚��Z¥Cg‚���[m���������PEAR/PackageFile/Parser/v1.php�@��Z¥Cg�@��c‚=m���������PEAR/PackageFile/Parser/v2.phpw ��Z¥Cgw ��ÎÎ7m���������PEAR/PackageFile/v1.phpþÅ��Z¥CgþÅ��£Q •m���������PEAR/PackageFile/v2.php�Z¥Cg��‰/m������!���PEAR/PackageFile/v2/Validator.php€L�Z¥Cg€L�Cø8m���������PEAR/Proxy.phpê��Z¥Cgê��(PzÖm���������PEAR/Registry.php¬*�Z¥Cg¬*�X?*m������ ���PEAR/REST.php�A��Z¥Cg�A��æÄm���������PEAR/REST/10.phpÜ��Z¥CgÜ��xÄ_èm���������PEAR/Start.phpÐ9��Z¥CgÐ9��
  1237. õ®m���������PEAR/Start/CLI.phpHS��Z¥CgHS��¥ÍáÃm���������PEAR/Task/Common.php8��Z¥Cg8��Qjc9m���������PEAR/Task/Postinstallscript.phpª9��Z¥Cgª9��ÍWy$m������"���PEAR/Task/Postinstallscript/rw.php5��Z¥Cg5��`‰\^m���������PEAR/Task/Replace.php��Z¥Cg��X,Ìm���������PEAR/Task/Replace/rw.php0��Z¥Cg0��SØZôm���������PEAR/Task/Unixeol.php ��Z¥Cg ��Tm¬Àm���������PEAR/Task/Unixeol/rw.php5��Z¥Cg5��í�œdm���������PEAR/Task/Windowseol.php ��Z¥Cg ��}�+úm���������PEAR/Task/Windowseol/rw.phpB��Z¥CgB��eIk,m���������PEAR/Validate.php'V��Z¥Cg'V��³=tëm���������PEAR/Validator/PECL.phpR��Z¥CgR��C€ÛSm���������PEAR/XMLParser.php1��Z¥Cg1��„Õm���������Structures/Graph.phpQ��Z¥CgQ��4´T·m������,���Structures/Graph/Manipulator/AcyclicTest.phpÌ��Z¥CgÌ�� Ò;ÿm������2���Structures/Graph/Manipulator/TopologicalSorter.phpØ��Z¥CgØ��3¤sm���������Structures/Graph/Node.phpR+��Z¥CgR+��5~ém������
  1238. ���System.phpQ��Z¥CgQ��oÞ�m������ ���XML/Util.phpÉ}��Z¥CgÉ}�� i¥mm������<?php
  1239. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  1240. /**
  1241. * File::CSV
  1242. *
  1243. * PHP versions 4 and 5
  1244. *
  1245. * Copyright (c) 1997-2008,
  1246. * Vincent Blavet <vincent@phpconcept.net>
  1247. * All rights reserved.
  1248. *
  1249. * Redistribution and use in source and binary forms, with or without
  1250. * modification, are permitted provided that the following conditions are met:
  1251. *
  1252. * * Redistributions of source code must retain the above copyright notice,
  1253. * this list of conditions and the following disclaimer.
  1254. * * Redistributions in binary form must reproduce the above copyright
  1255. * notice, this list of conditions and the following disclaimer in the
  1256. * documentation and/or other materials provided with the distribution.
  1257. *
  1258. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  1259. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  1260. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  1261. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
  1262. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  1263. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  1264. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  1265. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  1266. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  1267. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  1268. *
  1269. * @category File_Formats
  1270. * @package Archive_Tar
  1271. * @author Vincent Blavet <vincent@phpconcept.net>
  1272. * @copyright 1997-2010 The Authors
  1273. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  1274. * @version CVS: $Id$
  1275. * @link http://pear.php.net/package/Archive_Tar
  1276. */
  1277. // If the PEAR class cannot be loaded via the autoloader,
  1278. // then try to require_once it from the PHP include path.
  1279. if (!class_exists('PEAR')) {
  1280. require_once 'phar://go-pear.phar/' . 'PEAR.php';
  1281. }
  1282. define('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
  1283. define('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));
  1284. if (!function_exists('gzopen') && function_exists('gzopen64')) {
  1285. function gzopen($filename, $mode, $use_include_path = 0)
  1286. {
  1287. return gzopen64($filename, $mode, $use_include_path);
  1288. }
  1289. }
  1290. if (!function_exists('gztell') && function_exists('gztell64')) {
  1291. function gztell($zp)
  1292. {
  1293. return gztell64($zp);
  1294. }
  1295. }
  1296. if (!function_exists('gzseek') && function_exists('gzseek64')) {
  1297. function gzseek($zp, $offset, $whence = SEEK_SET)
  1298. {
  1299. return gzseek64($zp, $offset, $whence);
  1300. }
  1301. }
  1302. /**
  1303. * Creates a (compressed) Tar archive
  1304. *
  1305. * @package Archive_Tar
  1306. * @author Vincent Blavet <vincent@phpconcept.net>
  1307. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  1308. * @version $Revision$
  1309. */
  1310. class Archive_Tar extends PEAR
  1311. {
  1312. /**
  1313. * @var string Name of the Tar
  1314. */
  1315. public $_tarname = '';
  1316. /**
  1317. * @var boolean if true, the Tar file will be gzipped
  1318. */
  1319. public $_compress = false;
  1320. /**
  1321. * @var string Type of compression : 'none', 'gz', 'bz2' or 'lzma2'
  1322. */
  1323. public $_compress_type = 'none';
  1324. /**
  1325. * @var string Explode separator
  1326. */
  1327. public $_separator = ' ';
  1328. /**
  1329. * @var file descriptor
  1330. */
  1331. public $_file = 0;
  1332. /**
  1333. * @var string Local Tar name of a remote Tar (http:// or ftp://)
  1334. */
  1335. public $_temp_tarname = '';
  1336. /**
  1337. * @var string regular expression for ignoring files or directories
  1338. */
  1339. public $_ignore_regexp = '';
  1340. /**
  1341. * @var object PEAR_Error object
  1342. */
  1343. public $error_object = null;
  1344. /**
  1345. * Format for data extraction
  1346. *
  1347. * @var string
  1348. */
  1349. public $_fmt = '';
  1350. /**
  1351. * @var int Length of the read buffer in bytes
  1352. */
  1353. protected $buffer_length;
  1354. /**
  1355. * Archive_Tar Class constructor. This flavour of the constructor only
  1356. * declare a new Archive_Tar object, identifying it by the name of the
  1357. * tar file.
  1358. * If the compress argument is set the tar will be read or created as a
  1359. * gzip or bz2 compressed TAR file.
  1360. *
  1361. * @param string $p_tarname The name of the tar archive to create
  1362. * @param string $p_compress can be null, 'gz', 'bz2' or 'lzma2'. This
  1363. * parameter indicates if gzip, bz2 or lzma2 compression
  1364. * is required. For compatibility reason the
  1365. * boolean value 'true' means 'gz'.
  1366. * @param int $buffer_length Length of the read buffer in bytes
  1367. *
  1368. * @return bool
  1369. */
  1370. public function __construct($p_tarname, $p_compress = null, $buffer_length = 512)
  1371. {
  1372. parent::__construct();
  1373. $this->_compress = false;
  1374. $this->_compress_type = 'none';
  1375. if (($p_compress === null) || ($p_compress == '')) {
  1376. if (@file_exists($p_tarname)) {
  1377. if ($fp = @fopen($p_tarname, "rb")) {
  1378. // look for gzip magic cookie
  1379. $data = fread($fp, 2);
  1380. fclose($fp);
  1381. if ($data == "\37\213") {
  1382. $this->_compress = true;
  1383. $this->_compress_type = 'gz';
  1384. // No sure it's enought for a magic code ....
  1385. } elseif ($data == "BZ") {
  1386. $this->_compress = true;
  1387. $this->_compress_type = 'bz2';
  1388. } elseif (file_get_contents($p_tarname, false, null, 1, 4) == '7zXZ') {
  1389. $this->_compress = true;
  1390. $this->_compress_type = 'lzma2';
  1391. }
  1392. }
  1393. } else {
  1394. // probably a remote file or some file accessible
  1395. // through a stream interface
  1396. if (substr($p_tarname, -2) == 'gz') {
  1397. $this->_compress = true;
  1398. $this->_compress_type = 'gz';
  1399. } elseif ((substr($p_tarname, -3) == 'bz2') ||
  1400. (substr($p_tarname, -2) == 'bz')
  1401. ) {
  1402. $this->_compress = true;
  1403. $this->_compress_type = 'bz2';
  1404. } else {
  1405. if (substr($p_tarname, -2) == 'xz') {
  1406. $this->_compress = true;
  1407. $this->_compress_type = 'lzma2';
  1408. }
  1409. }
  1410. }
  1411. } else {
  1412. if (($p_compress === true) || ($p_compress == 'gz')) {
  1413. $this->_compress = true;
  1414. $this->_compress_type = 'gz';
  1415. } else {
  1416. if ($p_compress == 'bz2') {
  1417. $this->_compress = true;
  1418. $this->_compress_type = 'bz2';
  1419. } else {
  1420. if ($p_compress == 'lzma2') {
  1421. $this->_compress = true;
  1422. $this->_compress_type = 'lzma2';
  1423. } else {
  1424. $this->_error(
  1425. "Unsupported compression type '$p_compress'\n" .
  1426. "Supported types are 'gz', 'bz2' and 'lzma2'.\n"
  1427. );
  1428. return false;
  1429. }
  1430. }
  1431. }
  1432. }
  1433. $this->_tarname = $p_tarname;
  1434. if ($this->_compress) { // assert zlib or bz2 or xz extension support
  1435. if ($this->_compress_type == 'gz') {
  1436. $extname = 'zlib';
  1437. } else {
  1438. if ($this->_compress_type == 'bz2') {
  1439. $extname = 'bz2';
  1440. } else {
  1441. if ($this->_compress_type == 'lzma2') {
  1442. $extname = 'xz';
  1443. }
  1444. }
  1445. }
  1446. if (!extension_loaded($extname)) {
  1447. PEAR::loadExtension($extname);
  1448. }
  1449. if (!extension_loaded($extname)) {
  1450. $this->_error(
  1451. "The extension '$extname' couldn't be found.\n" .
  1452. "Please make sure your version of PHP was built " .
  1453. "with '$extname' support.\n"
  1454. );
  1455. return false;
  1456. }
  1457. }
  1458. if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) {
  1459. $this->_fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
  1460. "a8checksum/a1typeflag/a100link/a6magic/a2version/" .
  1461. "a32uname/a32gname/a8devmajor/a8devminor/a131prefix";
  1462. } else {
  1463. $this->_fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" .
  1464. "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" .
  1465. "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix";
  1466. }
  1467. $this->buffer_length = $buffer_length;
  1468. }
  1469. public function __destruct()
  1470. {
  1471. $this->_close();
  1472. // ----- Look for a local copy to delete
  1473. if ($this->_temp_tarname != '' && (bool) preg_match('/^tar[[:alnum:]]*\.tmp$/', $this->_temp_tarname)) {
  1474. @unlink($this->_temp_tarname);
  1475. }
  1476. }
  1477. /**
  1478. * This method creates the archive file and add the files / directories
  1479. * that are listed in $p_filelist.
  1480. * If a file with the same name exist and is writable, it is replaced
  1481. * by the new tar.
  1482. * The method return false and a PEAR error text.
  1483. * The $p_filelist parameter can be an array of string, each string
  1484. * representing a filename or a directory name with their path if
  1485. * needed. It can also be a single string with names separated by a
  1486. * single blank.
  1487. * For each directory added in the archive, the files and
  1488. * sub-directories are also added.
  1489. * See also createModify() method for more details.
  1490. *
  1491. * @param array $p_filelist An array of filenames and directory names, or a
  1492. * single string with names separated by a single
  1493. * blank space.
  1494. *
  1495. * @return bool true on success, false on error.
  1496. * @see createModify()
  1497. */
  1498. public function create($p_filelist)
  1499. {
  1500. return $this->createModify($p_filelist, '', '');
  1501. }
  1502. /**
  1503. * This method add the files / directories that are listed in $p_filelist in
  1504. * the archive. If the archive does not exist it is created.
  1505. * The method return false and a PEAR error text.
  1506. * The files and directories listed are only added at the end of the archive,
  1507. * even if a file with the same name is already archived.
  1508. * See also createModify() method for more details.
  1509. *
  1510. * @param array $p_filelist An array of filenames and directory names, or a
  1511. * single string with names separated by a single
  1512. * blank space.
  1513. *
  1514. * @return bool true on success, false on error.
  1515. * @see createModify()
  1516. * @access public
  1517. */
  1518. public function add($p_filelist)
  1519. {
  1520. return $this->addModify($p_filelist, '', '');
  1521. }
  1522. /**
  1523. * @param string $p_path
  1524. * @param bool $p_preserve
  1525. * @param bool $p_symlinks
  1526. * @return bool
  1527. */
  1528. public function extract($p_path = '', $p_preserve = false, $p_symlinks = true)
  1529. {
  1530. return $this->extractModify($p_path, '', $p_preserve, $p_symlinks);
  1531. }
  1532. /**
  1533. * @return array|int
  1534. */
  1535. public function listContent()
  1536. {
  1537. $v_list_detail = array();
  1538. if ($this->_openRead()) {
  1539. if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
  1540. unset($v_list_detail);
  1541. $v_list_detail = 0;
  1542. }
  1543. $this->_close();
  1544. }
  1545. return $v_list_detail;
  1546. }
  1547. /**
  1548. * This method creates the archive file and add the files / directories
  1549. * that are listed in $p_filelist.
  1550. * If the file already exists and is writable, it is replaced by the
  1551. * new tar. It is a create and not an add. If the file exists and is
  1552. * read-only or is a directory it is not replaced. The method return
  1553. * false and a PEAR error text.
  1554. * The $p_filelist parameter can be an array of string, each string
  1555. * representing a filename or a directory name with their path if
  1556. * needed. It can also be a single string with names separated by a
  1557. * single blank.
  1558. * The path indicated in $p_remove_dir will be removed from the
  1559. * memorized path of each file / directory listed when this path
  1560. * exists. By default nothing is removed (empty path '')
  1561. * The path indicated in $p_add_dir will be added at the beginning of
  1562. * the memorized path of each file / directory listed. However it can
  1563. * be set to empty ''. The adding of a path is done after the removing
  1564. * of path.
  1565. * The path add/remove ability enables the user to prepare an archive
  1566. * for extraction in a different path than the origin files are.
  1567. * See also addModify() method for file adding properties.
  1568. *
  1569. * @param array $p_filelist An array of filenames and directory names,
  1570. * or a single string with names separated by
  1571. * a single blank space.
  1572. * @param string $p_add_dir A string which contains a path to be added
  1573. * to the memorized path of each element in
  1574. * the list.
  1575. * @param string $p_remove_dir A string which contains a path to be
  1576. * removed from the memorized path of each
  1577. * element in the list, when relevant.
  1578. *
  1579. * @return boolean true on success, false on error.
  1580. * @see addModify()
  1581. */
  1582. public function createModify($p_filelist, $p_add_dir, $p_remove_dir = '')
  1583. {
  1584. $v_result = true;
  1585. if (!$this->_openWrite()) {
  1586. return false;
  1587. }
  1588. if ($p_filelist != '') {
  1589. if (is_array($p_filelist)) {
  1590. $v_list = $p_filelist;
  1591. } elseif (is_string($p_filelist)) {
  1592. $v_list = explode($this->_separator, $p_filelist);
  1593. } else {
  1594. $this->_cleanFile();
  1595. $this->_error('Invalid file list');
  1596. return false;
  1597. }
  1598. $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
  1599. }
  1600. if ($v_result) {
  1601. $this->_writeFooter();
  1602. $this->_close();
  1603. } else {
  1604. $this->_cleanFile();
  1605. }
  1606. return $v_result;
  1607. }
  1608. /**
  1609. * This method add the files / directories listed in $p_filelist at the
  1610. * end of the existing archive. If the archive does not yet exists it
  1611. * is created.
  1612. * The $p_filelist parameter can be an array of string, each string
  1613. * representing a filename or a directory name with their path if
  1614. * needed. It can also be a single string with names separated by a
  1615. * single blank.
  1616. * The path indicated in $p_remove_dir will be removed from the
  1617. * memorized path of each file / directory listed when this path
  1618. * exists. By default nothing is removed (empty path '')
  1619. * The path indicated in $p_add_dir will be added at the beginning of
  1620. * the memorized path of each file / directory listed. However it can
  1621. * be set to empty ''. The adding of a path is done after the removing
  1622. * of path.
  1623. * The path add/remove ability enables the user to prepare an archive
  1624. * for extraction in a different path than the origin files are.
  1625. * If a file/dir is already in the archive it will only be added at the
  1626. * end of the archive. There is no update of the existing archived
  1627. * file/dir. However while extracting the archive, the last file will
  1628. * replace the first one. This results in a none optimization of the
  1629. * archive size.
  1630. * If a file/dir does not exist the file/dir is ignored. However an
  1631. * error text is send to PEAR error.
  1632. * If a file/dir is not readable the file/dir is ignored. However an
  1633. * error text is send to PEAR error.
  1634. *
  1635. * @param array $p_filelist An array of filenames and directory
  1636. * names, or a single string with names
  1637. * separated by a single blank space.
  1638. * @param string $p_add_dir A string which contains a path to be
  1639. * added to the memorized path of each
  1640. * element in the list.
  1641. * @param string $p_remove_dir A string which contains a path to be
  1642. * removed from the memorized path of
  1643. * each element in the list, when
  1644. * relevant.
  1645. *
  1646. * @return bool true on success, false on error.
  1647. */
  1648. public function addModify($p_filelist, $p_add_dir, $p_remove_dir = '')
  1649. {
  1650. $v_result = true;
  1651. if (!$this->_isArchive()) {
  1652. $v_result = $this->createModify(
  1653. $p_filelist,
  1654. $p_add_dir,
  1655. $p_remove_dir
  1656. );
  1657. } else {
  1658. if (is_array($p_filelist)) {
  1659. $v_list = $p_filelist;
  1660. } elseif (is_string($p_filelist)) {
  1661. $v_list = explode($this->_separator, $p_filelist);
  1662. } else {
  1663. $this->_error('Invalid file list');
  1664. return false;
  1665. }
  1666. $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
  1667. }
  1668. return $v_result;
  1669. }
  1670. /**
  1671. * This method add a single string as a file at the
  1672. * end of the existing archive. If the archive does not yet exists it
  1673. * is created.
  1674. *
  1675. * @param string $p_filename A string which contains the full
  1676. * filename path that will be associated
  1677. * with the string.
  1678. * @param string $p_string The content of the file added in
  1679. * the archive.
  1680. * @param bool|int $p_datetime A custom date/time (unix timestamp)
  1681. * for the file (optional).
  1682. * @param array $p_params An array of optional params:
  1683. * stamp => the datetime (replaces
  1684. * datetime above if it exists)
  1685. * mode => the permissions on the
  1686. * file (600 by default)
  1687. * type => is this a link? See the
  1688. * tar specification for details.
  1689. * (default = regular file)
  1690. * uid => the user ID of the file
  1691. * (default = 0 = root)
  1692. * gid => the group ID of the file
  1693. * (default = 0 = root)
  1694. *
  1695. * @return bool true on success, false on error.
  1696. */
  1697. public function addString($p_filename, $p_string, $p_datetime = false, $p_params = array())
  1698. {
  1699. $p_stamp = @$p_params["stamp"] ? $p_params["stamp"] : ($p_datetime ? $p_datetime : time());
  1700. $p_mode = @$p_params["mode"] ? $p_params["mode"] : 0600;
  1701. $p_type = @$p_params["type"] ? $p_params["type"] : "";
  1702. $p_uid = @$p_params["uid"] ? $p_params["uid"] : "";
  1703. $p_gid = @$p_params["gid"] ? $p_params["gid"] : "";
  1704. $v_result = true;
  1705. if (!$this->_isArchive()) {
  1706. if (!$this->_openWrite()) {
  1707. return false;
  1708. }
  1709. $this->_close();
  1710. }
  1711. if (!$this->_openAppend()) {
  1712. return false;
  1713. }
  1714. // Need to check the get back to the temporary file ? ....
  1715. $v_result = $this->_addString($p_filename, $p_string, $p_datetime, $p_params);
  1716. $this->_writeFooter();
  1717. $this->_close();
  1718. return $v_result;
  1719. }
  1720. /**
  1721. * This method extract all the content of the archive in the directory
  1722. * indicated by $p_path. When relevant the memorized path of the
  1723. * files/dir can be modified by removing the $p_remove_path path at the
  1724. * beginning of the file/dir path.
  1725. * While extracting a file, if the directory path does not exists it is
  1726. * created.
  1727. * While extracting a file, if the file already exists it is replaced
  1728. * without looking for last modification date.
  1729. * While extracting a file, if the file already exists and is write
  1730. * protected, the extraction is aborted.
  1731. * While extracting a file, if a directory with the same name already
  1732. * exists, the extraction is aborted.
  1733. * While extracting a directory, if a file with the same name already
  1734. * exists, the extraction is aborted.
  1735. * While extracting a file/directory if the destination directory exist
  1736. * and is write protected, or does not exist but can not be created,
  1737. * the extraction is aborted.
  1738. * If after extraction an extracted file does not show the correct
  1739. * stored file size, the extraction is aborted.
  1740. * When the extraction is aborted, a PEAR error text is set and false
  1741. * is returned. However the result can be a partial extraction that may
  1742. * need to be manually cleaned.
  1743. *
  1744. * @param string $p_path The path of the directory where the
  1745. * files/dir need to by extracted.
  1746. * @param string $p_remove_path Part of the memorized path that can be
  1747. * removed if present at the beginning of
  1748. * the file/dir path.
  1749. * @param boolean $p_preserve Preserve user/group ownership of files
  1750. * @param boolean $p_symlinks Allow symlinks.
  1751. *
  1752. * @return boolean true on success, false on error.
  1753. * @see extractList()
  1754. */
  1755. public function extractModify($p_path, $p_remove_path, $p_preserve = false, $p_symlinks = true)
  1756. {
  1757. $v_result = true;
  1758. $v_list_detail = array();
  1759. if ($v_result = $this->_openRead()) {
  1760. $v_result = $this->_extractList(
  1761. $p_path,
  1762. $v_list_detail,
  1763. "complete",
  1764. 0,
  1765. $p_remove_path,
  1766. $p_preserve,
  1767. $p_symlinks
  1768. );
  1769. $this->_close();
  1770. }
  1771. return $v_result;
  1772. }
  1773. /**
  1774. * This method extract from the archive one file identified by $p_filename.
  1775. * The return value is a string with the file content, or NULL on error.
  1776. *
  1777. * @param string $p_filename The path of the file to extract in a string.
  1778. *
  1779. * @return a string with the file content or NULL.
  1780. */
  1781. public function extractInString($p_filename)
  1782. {
  1783. if ($this->_openRead()) {
  1784. $v_result = $this->_extractInString($p_filename);
  1785. $this->_close();
  1786. } else {
  1787. $v_result = null;
  1788. }
  1789. return $v_result;
  1790. }
  1791. /**
  1792. * This method extract from the archive only the files indicated in the
  1793. * $p_filelist. These files are extracted in the current directory or
  1794. * in the directory indicated by the optional $p_path parameter.
  1795. * If indicated the $p_remove_path can be used in the same way as it is
  1796. * used in extractModify() method.
  1797. *
  1798. * @param array $p_filelist An array of filenames and directory names,
  1799. * or a single string with names separated
  1800. * by a single blank space.
  1801. * @param string $p_path The path of the directory where the
  1802. * files/dir need to by extracted.
  1803. * @param string $p_remove_path Part of the memorized path that can be
  1804. * removed if present at the beginning of
  1805. * the file/dir path.
  1806. * @param boolean $p_preserve Preserve user/group ownership of files
  1807. * @param boolean $p_symlinks Allow symlinks.
  1808. *
  1809. * @return bool true on success, false on error.
  1810. * @see extractModify()
  1811. */
  1812. public function extractList($p_filelist, $p_path = '', $p_remove_path = '', $p_preserve = false, $p_symlinks = true)
  1813. {
  1814. $v_result = true;
  1815. $v_list_detail = array();
  1816. if (is_array($p_filelist)) {
  1817. $v_list = $p_filelist;
  1818. } elseif (is_string($p_filelist)) {
  1819. $v_list = explode($this->_separator, $p_filelist);
  1820. } else {
  1821. $this->_error('Invalid string list');
  1822. return false;
  1823. }
  1824. if ($v_result = $this->_openRead()) {
  1825. $v_result = $this->_extractList(
  1826. $p_path,
  1827. $v_list_detail,
  1828. "partial",
  1829. $v_list,
  1830. $p_remove_path,
  1831. $p_preserve,
  1832. $p_symlinks
  1833. );
  1834. $this->_close();
  1835. }
  1836. return $v_result;
  1837. }
  1838. /**
  1839. * This method set specific attributes of the archive. It uses a variable
  1840. * list of parameters, in the format attribute code + attribute values :
  1841. * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ',');
  1842. *
  1843. * @return bool true on success, false on error.
  1844. */
  1845. public function setAttribute()
  1846. {
  1847. $v_result = true;
  1848. // ----- Get the number of variable list of arguments
  1849. if (($v_size = func_num_args()) == 0) {
  1850. return true;
  1851. }
  1852. // ----- Get the arguments
  1853. $v_att_list = func_get_args();
  1854. // ----- Read the attributes
  1855. $i = 0;
  1856. while ($i < $v_size) {
  1857. // ----- Look for next option
  1858. switch ($v_att_list[$i]) {
  1859. // ----- Look for options that request a string value
  1860. case ARCHIVE_TAR_ATT_SEPARATOR :
  1861. // ----- Check the number of parameters
  1862. if (($i + 1) >= $v_size) {
  1863. $this->_error(
  1864. 'Invalid number of parameters for '
  1865. . 'attribute ARCHIVE_TAR_ATT_SEPARATOR'
  1866. );
  1867. return false;
  1868. }
  1869. // ----- Get the value
  1870. $this->_separator = $v_att_list[$i + 1];
  1871. $i++;
  1872. break;
  1873. default :
  1874. $this->_error('Unknown attribute code ' . $v_att_list[$i] . '');
  1875. return false;
  1876. }
  1877. // ----- Next attribute
  1878. $i++;
  1879. }
  1880. return $v_result;
  1881. }
  1882. /**
  1883. * This method sets the regular expression for ignoring files and directories
  1884. * at import, for example:
  1885. * $arch->setIgnoreRegexp("#CVS|\.svn#");
  1886. *
  1887. * @param string $regexp regular expression defining which files or directories to ignore
  1888. */
  1889. public function setIgnoreRegexp($regexp)
  1890. {
  1891. $this->_ignore_regexp = $regexp;
  1892. }
  1893. /**
  1894. * This method sets the regular expression for ignoring all files and directories
  1895. * matching the filenames in the array list at import, for example:
  1896. * $arch->setIgnoreList(array('CVS', '.svn', 'bin/tool'));
  1897. *
  1898. * @param array $list a list of file or directory names to ignore
  1899. *
  1900. * @access public
  1901. */
  1902. public function setIgnoreList($list)
  1903. {
  1904. $list = str_replace(array('#', '.', '^', '$'), array('\#', '\.', '\^', '\$'), $list);
  1905. $regexp = '#/' . join('$|/', $list) . '#';
  1906. $this->setIgnoreRegexp($regexp);
  1907. }
  1908. /**
  1909. * @param string $p_message
  1910. */
  1911. public function _error($p_message)
  1912. {
  1913. $this->error_object = $this->raiseError($p_message);
  1914. }
  1915. /**
  1916. * @param string $p_message
  1917. */
  1918. public function _warning($p_message)
  1919. {
  1920. $this->error_object = $this->raiseError($p_message);
  1921. }
  1922. /**
  1923. * @param string $p_filename
  1924. * @return bool
  1925. */
  1926. public function _isArchive($p_filename = null)
  1927. {
  1928. if ($p_filename == null) {
  1929. $p_filename = $this->_tarname;
  1930. }
  1931. clearstatcache();
  1932. return @is_file($p_filename) && !@is_link($p_filename);
  1933. }
  1934. /**
  1935. * @return bool
  1936. */
  1937. public function _openWrite()
  1938. {
  1939. if ($this->_compress_type == 'gz' && function_exists('gzopen')) {
  1940. $this->_file = @gzopen($this->_tarname, "wb9");
  1941. } else {
  1942. if ($this->_compress_type == 'bz2' && function_exists('bzopen')) {
  1943. $this->_file = @bzopen($this->_tarname, "w");
  1944. } else {
  1945. if ($this->_compress_type == 'lzma2' && function_exists('xzopen')) {
  1946. $this->_file = @xzopen($this->_tarname, 'w');
  1947. } else {
  1948. if ($this->_compress_type == 'none') {
  1949. $this->_file = @fopen($this->_tarname, "wb");
  1950. } else {
  1951. $this->_error(
  1952. 'Unknown or missing compression type ('
  1953. . $this->_compress_type . ')'
  1954. );
  1955. return false;
  1956. }
  1957. }
  1958. }
  1959. }
  1960. if ($this->_file == 0) {
  1961. $this->_error(
  1962. 'Unable to open in write mode \''
  1963. . $this->_tarname . '\''
  1964. );
  1965. return false;
  1966. }
  1967. return true;
  1968. }
  1969. /**
  1970. * @return bool
  1971. */
  1972. public function _openRead()
  1973. {
  1974. if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
  1975. // ----- Look if a local copy need to be done
  1976. if ($this->_temp_tarname == '') {
  1977. $this->_temp_tarname = uniqid('tar') . '.tmp';
  1978. if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
  1979. $this->_error(
  1980. 'Unable to open in read mode \''
  1981. . $this->_tarname . '\''
  1982. );
  1983. $this->_temp_tarname = '';
  1984. return false;
  1985. }
  1986. if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
  1987. $this->_error(
  1988. 'Unable to open in write mode \''
  1989. . $this->_temp_tarname . '\''
  1990. );
  1991. $this->_temp_tarname = '';
  1992. return false;
  1993. }
  1994. while ($v_data = @fread($v_file_from, 1024)) {
  1995. @fwrite($v_file_to, $v_data);
  1996. }
  1997. @fclose($v_file_from);
  1998. @fclose($v_file_to);
  1999. }
  2000. // ----- File to open if the local copy
  2001. $v_filename = $this->_temp_tarname;
  2002. } else {
  2003. // ----- File to open if the normal Tar file
  2004. $v_filename = $this->_tarname;
  2005. }
  2006. if ($this->_compress_type == 'gz' && function_exists('gzopen')) {
  2007. $this->_file = @gzopen($v_filename, "rb");
  2008. } else {
  2009. if ($this->_compress_type == 'bz2' && function_exists('bzopen')) {
  2010. $this->_file = @bzopen($v_filename, "r");
  2011. } else {
  2012. if ($this->_compress_type == 'lzma2' && function_exists('xzopen')) {
  2013. $this->_file = @xzopen($v_filename, "r");
  2014. } else {
  2015. if ($this->_compress_type == 'none') {
  2016. $this->_file = @fopen($v_filename, "rb");
  2017. } else {
  2018. $this->_error(
  2019. 'Unknown or missing compression type ('
  2020. . $this->_compress_type . ')'
  2021. );
  2022. return false;
  2023. }
  2024. }
  2025. }
  2026. }
  2027. if ($this->_file == 0) {
  2028. $this->_error('Unable to open in read mode \'' . $v_filename . '\'');
  2029. return false;
  2030. }
  2031. return true;
  2032. }
  2033. /**
  2034. * @return bool
  2035. */
  2036. public function _openReadWrite()
  2037. {
  2038. if ($this->_compress_type == 'gz') {
  2039. $this->_file = @gzopen($this->_tarname, "r+b");
  2040. } else {
  2041. if ($this->_compress_type == 'bz2') {
  2042. $this->_error(
  2043. 'Unable to open bz2 in read/write mode \''
  2044. . $this->_tarname . '\' (limitation of bz2 extension)'
  2045. );
  2046. return false;
  2047. } else {
  2048. if ($this->_compress_type == 'lzma2') {
  2049. $this->_error(
  2050. 'Unable to open lzma2 in read/write mode \''
  2051. . $this->_tarname . '\' (limitation of lzma2 extension)'
  2052. );
  2053. return false;
  2054. } else {
  2055. if ($this->_compress_type == 'none') {
  2056. $this->_file = @fopen($this->_tarname, "r+b");
  2057. } else {
  2058. $this->_error(
  2059. 'Unknown or missing compression type ('
  2060. . $this->_compress_type . ')'
  2061. );
  2062. return false;
  2063. }
  2064. }
  2065. }
  2066. }
  2067. if ($this->_file == 0) {
  2068. $this->_error(
  2069. 'Unable to open in read/write mode \''
  2070. . $this->_tarname . '\''
  2071. );
  2072. return false;
  2073. }
  2074. return true;
  2075. }
  2076. /**
  2077. * @return bool
  2078. */
  2079. public function _close()
  2080. {
  2081. //if (isset($this->_file)) {
  2082. if (is_resource($this->_file)) {
  2083. if ($this->_compress_type == 'gz') {
  2084. @gzclose($this->_file);
  2085. } else {
  2086. if ($this->_compress_type == 'bz2') {
  2087. @bzclose($this->_file);
  2088. } else {
  2089. if ($this->_compress_type == 'lzma2') {
  2090. @xzclose($this->_file);
  2091. } else {
  2092. if ($this->_compress_type == 'none') {
  2093. @fclose($this->_file);
  2094. } else {
  2095. $this->_error(
  2096. 'Unknown or missing compression type ('
  2097. . $this->_compress_type . ')'
  2098. );
  2099. }
  2100. }
  2101. }
  2102. }
  2103. $this->_file = 0;
  2104. }
  2105. // ----- Look if a local copy need to be erase
  2106. // Note that it might be interesting to keep the url for a time : ToDo
  2107. if ($this->_temp_tarname != '') {
  2108. @unlink($this->_temp_tarname);
  2109. $this->_temp_tarname = '';
  2110. }
  2111. return true;
  2112. }
  2113. /**
  2114. * @return bool
  2115. */
  2116. public function _cleanFile()
  2117. {
  2118. $this->_close();
  2119. // ----- Look for a local copy
  2120. if ($this->_temp_tarname != '') {
  2121. // ----- Remove the local copy but not the remote tarname
  2122. @unlink($this->_temp_tarname);
  2123. $this->_temp_tarname = '';
  2124. } else {
  2125. // ----- Remove the local tarname file
  2126. @unlink($this->_tarname);
  2127. }
  2128. $this->_tarname = '';
  2129. return true;
  2130. }
  2131. /**
  2132. * @param mixed $p_binary_data
  2133. * @param integer $p_len
  2134. * @return bool
  2135. */
  2136. public function _writeBlock($p_binary_data, $p_len = null)
  2137. {
  2138. if (is_resource($this->_file)) {
  2139. if ($p_len === null) {
  2140. if ($this->_compress_type == 'gz') {
  2141. @gzputs($this->_file, $p_binary_data);
  2142. } else {
  2143. if ($this->_compress_type == 'bz2') {
  2144. @bzwrite($this->_file, $p_binary_data);
  2145. } else {
  2146. if ($this->_compress_type == 'lzma2') {
  2147. @xzwrite($this->_file, $p_binary_data);
  2148. } else {
  2149. if ($this->_compress_type == 'none') {
  2150. @fputs($this->_file, $p_binary_data);
  2151. } else {
  2152. $this->_error(
  2153. 'Unknown or missing compression type ('
  2154. . $this->_compress_type . ')'
  2155. );
  2156. }
  2157. }
  2158. }
  2159. }
  2160. } else {
  2161. if ($this->_compress_type == 'gz') {
  2162. @gzputs($this->_file, $p_binary_data, $p_len);
  2163. } else {
  2164. if ($this->_compress_type == 'bz2') {
  2165. @bzwrite($this->_file, $p_binary_data, $p_len);
  2166. } else {
  2167. if ($this->_compress_type == 'lzma2') {
  2168. @xzwrite($this->_file, $p_binary_data, $p_len);
  2169. } else {
  2170. if ($this->_compress_type == 'none') {
  2171. @fputs($this->_file, $p_binary_data, $p_len);
  2172. } else {
  2173. $this->_error(
  2174. 'Unknown or missing compression type ('
  2175. . $this->_compress_type . ')'
  2176. );
  2177. }
  2178. }
  2179. }
  2180. }
  2181. }
  2182. }
  2183. return true;
  2184. }
  2185. /**
  2186. * @return null|string
  2187. */
  2188. public function _readBlock()
  2189. {
  2190. $v_block = null;
  2191. if (is_resource($this->_file)) {
  2192. if ($this->_compress_type == 'gz') {
  2193. $v_block = @gzread($this->_file, 512);
  2194. } else {
  2195. if ($this->_compress_type == 'bz2') {
  2196. $v_block = @bzread($this->_file, 512);
  2197. } else {
  2198. if ($this->_compress_type == 'lzma2') {
  2199. $v_block = @xzread($this->_file, 512);
  2200. } else {
  2201. if ($this->_compress_type == 'none') {
  2202. $v_block = @fread($this->_file, 512);
  2203. } else {
  2204. $this->_error(
  2205. 'Unknown or missing compression type ('
  2206. . $this->_compress_type . ')'
  2207. );
  2208. }
  2209. }
  2210. }
  2211. }
  2212. }
  2213. return $v_block;
  2214. }
  2215. /**
  2216. * @param null $p_len
  2217. * @return bool
  2218. */
  2219. public function _jumpBlock($p_len = null)
  2220. {
  2221. if (is_resource($this->_file)) {
  2222. if ($p_len === null) {
  2223. $p_len = 1;
  2224. }
  2225. if ($this->_compress_type == 'gz') {
  2226. @gzseek($this->_file, gztell($this->_file) + ($p_len * 512));
  2227. } else {
  2228. if ($this->_compress_type == 'bz2') {
  2229. // ----- Replace missing bztell() and bzseek()
  2230. for ($i = 0; $i < $p_len; $i++) {
  2231. $this->_readBlock();
  2232. }
  2233. } else {
  2234. if ($this->_compress_type == 'lzma2') {
  2235. // ----- Replace missing xztell() and xzseek()
  2236. for ($i = 0; $i < $p_len; $i++) {
  2237. $this->_readBlock();
  2238. }
  2239. } else {
  2240. if ($this->_compress_type == 'none') {
  2241. @fseek($this->_file, $p_len * 512, SEEK_CUR);
  2242. } else {
  2243. $this->_error(
  2244. 'Unknown or missing compression type ('
  2245. . $this->_compress_type . ')'
  2246. );
  2247. }
  2248. }
  2249. }
  2250. }
  2251. }
  2252. return true;
  2253. }
  2254. /**
  2255. * @return bool
  2256. */
  2257. public function _writeFooter()
  2258. {
  2259. if (is_resource($this->_file)) {
  2260. // ----- Write the last 0 filled block for end of archive
  2261. $v_binary_data = pack('a1024', '');
  2262. $this->_writeBlock($v_binary_data);
  2263. }
  2264. return true;
  2265. }
  2266. /**
  2267. * @param array $p_list
  2268. * @param string $p_add_dir
  2269. * @param string $p_remove_dir
  2270. * @return bool
  2271. */
  2272. public function _addList($p_list, $p_add_dir, $p_remove_dir)
  2273. {
  2274. $v_result = true;
  2275. $v_header = array();
  2276. // ----- Remove potential windows directory separator
  2277. $p_add_dir = $this->_translateWinPath($p_add_dir);
  2278. $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
  2279. if (!$this->_file) {
  2280. $this->_error('Invalid file descriptor');
  2281. return false;
  2282. }
  2283. if (sizeof($p_list) == 0) {
  2284. return true;
  2285. }
  2286. foreach ($p_list as $v_filename) {
  2287. if (!$v_result) {
  2288. break;
  2289. }
  2290. // ----- Skip the current tar name
  2291. if ($v_filename == $this->_tarname) {
  2292. continue;
  2293. }
  2294. if ($v_filename == '') {
  2295. continue;
  2296. }
  2297. // ----- ignore files and directories matching the ignore regular expression
  2298. if ($this->_ignore_regexp && preg_match($this->_ignore_regexp, '/' . $v_filename)) {
  2299. $this->_warning("File '$v_filename' ignored");
  2300. continue;
  2301. }
  2302. if (!file_exists($v_filename) && !is_link($v_filename)) {
  2303. $this->_warning("File '$v_filename' does not exist");
  2304. continue;
  2305. }
  2306. // ----- Add the file or directory header
  2307. if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir)) {
  2308. return false;
  2309. }
  2310. if (@is_dir($v_filename) && !@is_link($v_filename)) {
  2311. if (!($p_hdir = opendir($v_filename))) {
  2312. $this->_warning("Directory '$v_filename' can not be read");
  2313. continue;
  2314. }
  2315. while (false !== ($p_hitem = readdir($p_hdir))) {
  2316. if (($p_hitem != '.') && ($p_hitem != '..')) {
  2317. if ($v_filename != ".") {
  2318. $p_temp_list[0] = $v_filename . '/' . $p_hitem;
  2319. } else {
  2320. $p_temp_list[0] = $p_hitem;
  2321. }
  2322. $v_result = $this->_addList(
  2323. $p_temp_list,
  2324. $p_add_dir,
  2325. $p_remove_dir
  2326. );
  2327. }
  2328. }
  2329. unset($p_temp_list);
  2330. unset($p_hdir);
  2331. unset($p_hitem);
  2332. }
  2333. }
  2334. return $v_result;
  2335. }
  2336. /**
  2337. * @param string $p_filename
  2338. * @param mixed $p_header
  2339. * @param string $p_add_dir
  2340. * @param string $p_remove_dir
  2341. * @param null $v_stored_filename
  2342. * @return bool
  2343. */
  2344. public function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir, $v_stored_filename = null)
  2345. {
  2346. if (!$this->_file) {
  2347. $this->_error('Invalid file descriptor');
  2348. return false;
  2349. }
  2350. if ($p_filename == '') {
  2351. $this->_error('Invalid file name');
  2352. return false;
  2353. }
  2354. if (is_null($v_stored_filename)) {
  2355. // ----- Calculate the stored filename
  2356. $p_filename = $this->_translateWinPath($p_filename, false);
  2357. $v_stored_filename = $p_filename;
  2358. if (strcmp($p_filename, $p_remove_dir) == 0) {
  2359. return true;
  2360. }
  2361. if ($p_remove_dir != '') {
  2362. if (substr($p_remove_dir, -1) != '/') {
  2363. $p_remove_dir .= '/';
  2364. }
  2365. if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir) {
  2366. $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
  2367. }
  2368. }
  2369. $v_stored_filename = $this->_translateWinPath($v_stored_filename);
  2370. if ($p_add_dir != '') {
  2371. if (substr($p_add_dir, -1) == '/') {
  2372. $v_stored_filename = $p_add_dir . $v_stored_filename;
  2373. } else {
  2374. $v_stored_filename = $p_add_dir . '/' . $v_stored_filename;
  2375. }
  2376. }
  2377. $v_stored_filename = $this->_pathReduction($v_stored_filename);
  2378. }
  2379. if ($this->_isArchive($p_filename)) {
  2380. if (($v_file = @fopen($p_filename, "rb")) == 0) {
  2381. $this->_warning(
  2382. "Unable to open file '" . $p_filename
  2383. . "' in binary read mode"
  2384. );
  2385. return true;
  2386. }
  2387. if (!$this->_writeHeader($p_filename, $v_stored_filename)) {
  2388. return false;
  2389. }
  2390. while (($v_buffer = fread($v_file, $this->buffer_length)) != '') {
  2391. $buffer_length = strlen("$v_buffer");
  2392. if ($buffer_length != $this->buffer_length) {
  2393. $pack_size = ((int)($buffer_length / 512) + ($buffer_length % 512 !== 0 ? 1 : 0)) * 512;
  2394. $pack_format = sprintf('a%d', $pack_size);
  2395. } else {
  2396. $pack_format = sprintf('a%d', $this->buffer_length);
  2397. }
  2398. $v_binary_data = pack($pack_format, "$v_buffer");
  2399. $this->_writeBlock($v_binary_data);
  2400. }
  2401. fclose($v_file);
  2402. } else {
  2403. // ----- Only header for dir
  2404. if (!$this->_writeHeader($p_filename, $v_stored_filename)) {
  2405. return false;
  2406. }
  2407. }
  2408. return true;
  2409. }
  2410. /**
  2411. * @param string $p_filename
  2412. * @param string $p_string
  2413. * @param bool $p_datetime
  2414. * @param array $p_params
  2415. * @return bool
  2416. */
  2417. public function _addString($p_filename, $p_string, $p_datetime = false, $p_params = array())
  2418. {
  2419. $p_stamp = @$p_params["stamp"] ? $p_params["stamp"] : ($p_datetime ? $p_datetime : time());
  2420. $p_mode = @$p_params["mode"] ? $p_params["mode"] : 0600;
  2421. $p_type = @$p_params["type"] ? $p_params["type"] : "";
  2422. $p_uid = @$p_params["uid"] ? $p_params["uid"] : 0;
  2423. $p_gid = @$p_params["gid"] ? $p_params["gid"] : 0;
  2424. if (!$this->_file) {
  2425. $this->_error('Invalid file descriptor');
  2426. return false;
  2427. }
  2428. if ($p_filename == '') {
  2429. $this->_error('Invalid file name');
  2430. return false;
  2431. }
  2432. // ----- Calculate the stored filename
  2433. $p_filename = $this->_translateWinPath($p_filename, false);
  2434. // ----- If datetime is not specified, set current time
  2435. if ($p_datetime === false) {
  2436. $p_datetime = time();
  2437. }
  2438. if (!$this->_writeHeaderBlock(
  2439. $p_filename,
  2440. strlen($p_string),
  2441. $p_stamp,
  2442. $p_mode,
  2443. $p_type,
  2444. $p_uid,
  2445. $p_gid
  2446. )
  2447. ) {
  2448. return false;
  2449. }
  2450. $i = 0;
  2451. while (($v_buffer = substr($p_string, (($i++) * 512), 512)) != '') {
  2452. $v_binary_data = pack("a512", $v_buffer);
  2453. $this->_writeBlock($v_binary_data);
  2454. }
  2455. return true;
  2456. }
  2457. /**
  2458. * @param string $p_filename
  2459. * @param string $p_stored_filename
  2460. * @return bool
  2461. */
  2462. public function _writeHeader($p_filename, $p_stored_filename)
  2463. {
  2464. if ($p_stored_filename == '') {
  2465. $p_stored_filename = $p_filename;
  2466. }
  2467. $v_reduced_filename = $this->_pathReduction($p_stored_filename);
  2468. if (strlen($v_reduced_filename) > 99) {
  2469. if (!$this->_writeLongHeader($v_reduced_filename, false)) {
  2470. return false;
  2471. }
  2472. }
  2473. $v_linkname = '';
  2474. if (@is_link($p_filename)) {
  2475. $v_linkname = readlink($p_filename);
  2476. }
  2477. if (strlen($v_linkname) > 99) {
  2478. if (!$this->_writeLongHeader($v_linkname, true)) {
  2479. return false;
  2480. }
  2481. }
  2482. $v_info = lstat($p_filename);
  2483. $v_uid = sprintf("%07s", DecOct($v_info[4]));
  2484. $v_gid = sprintf("%07s", DecOct($v_info[5]));
  2485. $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777));
  2486. $v_mtime = sprintf("%011s", DecOct($v_info['mtime']));
  2487. if (@is_link($p_filename)) {
  2488. $v_typeflag = '2';
  2489. $v_size = sprintf("%011s", DecOct(0));
  2490. } elseif (@is_dir($p_filename)) {
  2491. $v_typeflag = "5";
  2492. $v_size = sprintf("%011s", DecOct(0));
  2493. } else {
  2494. $v_typeflag = '0';
  2495. clearstatcache();
  2496. $v_size = sprintf("%011s", DecOct($v_info['size']));
  2497. }
  2498. $v_magic = 'ustar ';
  2499. $v_version = ' ';
  2500. $v_uname = '';
  2501. $v_gname = '';
  2502. if (function_exists('posix_getpwuid')) {
  2503. $userinfo = posix_getpwuid($v_info[4]);
  2504. $groupinfo = posix_getgrgid($v_info[5]);
  2505. if (isset($userinfo['name'])) {
  2506. $v_uname = $userinfo['name'];
  2507. }
  2508. if (isset($groupinfo['name'])) {
  2509. $v_gname = $groupinfo['name'];
  2510. }
  2511. }
  2512. $v_devmajor = '';
  2513. $v_devminor = '';
  2514. $v_prefix = '';
  2515. $v_binary_data_first = pack(
  2516. "a100a8a8a8a12a12",
  2517. $v_reduced_filename,
  2518. $v_perms,
  2519. $v_uid,
  2520. $v_gid,
  2521. $v_size,
  2522. $v_mtime
  2523. );
  2524. $v_binary_data_last = pack(
  2525. "a1a100a6a2a32a32a8a8a155a12",
  2526. $v_typeflag,
  2527. $v_linkname,
  2528. $v_magic,
  2529. $v_version,
  2530. $v_uname,
  2531. $v_gname,
  2532. $v_devmajor,
  2533. $v_devminor,
  2534. $v_prefix,
  2535. ''
  2536. );
  2537. // ----- Calculate the checksum
  2538. $v_checksum = 0;
  2539. // ..... First part of the header
  2540. for ($i = 0; $i < 148; $i++) {
  2541. $v_checksum += ord(substr($v_binary_data_first, $i, 1));
  2542. }
  2543. // ..... Ignore the checksum value and replace it by ' ' (space)
  2544. for ($i = 148; $i < 156; $i++) {
  2545. $v_checksum += ord(' ');
  2546. }
  2547. // ..... Last part of the header
  2548. for ($i = 156, $j = 0; $i < 512; $i++, $j++) {
  2549. $v_checksum += ord(substr($v_binary_data_last, $j, 1));
  2550. }
  2551. // ----- Write the first 148 bytes of the header in the archive
  2552. $this->_writeBlock($v_binary_data_first, 148);
  2553. // ----- Write the calculated checksum
  2554. $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum));
  2555. $v_binary_data = pack("a8", $v_checksum);
  2556. $this->_writeBlock($v_binary_data, 8);
  2557. // ----- Write the last 356 bytes of the header in the archive
  2558. $this->_writeBlock($v_binary_data_last, 356);
  2559. return true;
  2560. }
  2561. /**
  2562. * @param string $p_filename
  2563. * @param int $p_size
  2564. * @param int $p_mtime
  2565. * @param int $p_perms
  2566. * @param string $p_type
  2567. * @param int $p_uid
  2568. * @param int $p_gid
  2569. * @return bool
  2570. */
  2571. public function _writeHeaderBlock(
  2572. $p_filename,
  2573. $p_size,
  2574. $p_mtime = 0,
  2575. $p_perms = 0,
  2576. $p_type = '',
  2577. $p_uid = 0,
  2578. $p_gid = 0
  2579. )
  2580. {
  2581. $p_filename = $this->_pathReduction($p_filename);
  2582. if (strlen($p_filename) > 99) {
  2583. if (!$this->_writeLongHeader($p_filename, false)) {
  2584. return false;
  2585. }
  2586. }
  2587. if ($p_type == "5") {
  2588. $v_size = sprintf("%011s", DecOct(0));
  2589. } else {
  2590. $v_size = sprintf("%011s", DecOct($p_size));
  2591. }
  2592. $v_uid = sprintf("%07s", DecOct($p_uid));
  2593. $v_gid = sprintf("%07s", DecOct($p_gid));
  2594. $v_perms = sprintf("%07s", DecOct($p_perms & 000777));
  2595. $v_mtime = sprintf("%11s", DecOct($p_mtime));
  2596. $v_linkname = '';
  2597. $v_magic = 'ustar ';
  2598. $v_version = ' ';
  2599. if (function_exists('posix_getpwuid')) {
  2600. $userinfo = posix_getpwuid($p_uid);
  2601. $groupinfo = posix_getgrgid($p_gid);
  2602. if ($userinfo === false || $groupinfo === false) {
  2603. $v_uname = '';
  2604. $v_gname = '';
  2605. } else {
  2606. $v_uname = $userinfo['name'];
  2607. $v_gname = $groupinfo['name'];
  2608. }
  2609. } else {
  2610. $v_uname = '';
  2611. $v_gname = '';
  2612. }
  2613. $v_devmajor = '';
  2614. $v_devminor = '';
  2615. $v_prefix = '';
  2616. $v_binary_data_first = pack(
  2617. "a100a8a8a8a12A12",
  2618. $p_filename,
  2619. $v_perms,
  2620. $v_uid,
  2621. $v_gid,
  2622. $v_size,
  2623. $v_mtime
  2624. );
  2625. $v_binary_data_last = pack(
  2626. "a1a100a6a2a32a32a8a8a155a12",
  2627. $p_type,
  2628. $v_linkname,
  2629. $v_magic,
  2630. $v_version,
  2631. $v_uname,
  2632. $v_gname,
  2633. $v_devmajor,
  2634. $v_devminor,
  2635. $v_prefix,
  2636. ''
  2637. );
  2638. // ----- Calculate the checksum
  2639. $v_checksum = 0;
  2640. // ..... First part of the header
  2641. for ($i = 0; $i < 148; $i++) {
  2642. $v_checksum += ord(substr($v_binary_data_first, $i, 1));
  2643. }
  2644. // ..... Ignore the checksum value and replace it by ' ' (space)
  2645. for ($i = 148; $i < 156; $i++) {
  2646. $v_checksum += ord(' ');
  2647. }
  2648. // ..... Last part of the header
  2649. for ($i = 156, $j = 0; $i < 512; $i++, $j++) {
  2650. $v_checksum += ord(substr($v_binary_data_last, $j, 1));
  2651. }
  2652. // ----- Write the first 148 bytes of the header in the archive
  2653. $this->_writeBlock($v_binary_data_first, 148);
  2654. // ----- Write the calculated checksum
  2655. $v_checksum = sprintf("%06s ", DecOct($v_checksum));
  2656. $v_binary_data = pack("a8", $v_checksum);
  2657. $this->_writeBlock($v_binary_data, 8);
  2658. // ----- Write the last 356 bytes of the header in the archive
  2659. $this->_writeBlock($v_binary_data_last, 356);
  2660. return true;
  2661. }
  2662. /**
  2663. * @param string $p_filename
  2664. * @return bool
  2665. */
  2666. public function _writeLongHeader($p_filename, $is_link = false)
  2667. {
  2668. $v_uid = sprintf("%07s", 0);
  2669. $v_gid = sprintf("%07s", 0);
  2670. $v_perms = sprintf("%07s", 0);
  2671. $v_size = sprintf("%'011s", DecOct(strlen($p_filename)));
  2672. $v_mtime = sprintf("%011s", 0);
  2673. $v_typeflag = ($is_link ? 'K' : 'L');
  2674. $v_linkname = '';
  2675. $v_magic = 'ustar ';
  2676. $v_version = ' ';
  2677. $v_uname = '';
  2678. $v_gname = '';
  2679. $v_devmajor = '';
  2680. $v_devminor = '';
  2681. $v_prefix = '';
  2682. $v_binary_data_first = pack(
  2683. "a100a8a8a8a12a12",
  2684. '././@LongLink',
  2685. $v_perms,
  2686. $v_uid,
  2687. $v_gid,
  2688. $v_size,
  2689. $v_mtime
  2690. );
  2691. $v_binary_data_last = pack(
  2692. "a1a100a6a2a32a32a8a8a155a12",
  2693. $v_typeflag,
  2694. $v_linkname,
  2695. $v_magic,
  2696. $v_version,
  2697. $v_uname,
  2698. $v_gname,
  2699. $v_devmajor,
  2700. $v_devminor,
  2701. $v_prefix,
  2702. ''
  2703. );
  2704. // ----- Calculate the checksum
  2705. $v_checksum = 0;
  2706. // ..... First part of the header
  2707. for ($i = 0; $i < 148; $i++) {
  2708. $v_checksum += ord(substr($v_binary_data_first, $i, 1));
  2709. }
  2710. // ..... Ignore the checksum value and replace it by ' ' (space)
  2711. for ($i = 148; $i < 156; $i++) {
  2712. $v_checksum += ord(' ');
  2713. }
  2714. // ..... Last part of the header
  2715. for ($i = 156, $j = 0; $i < 512; $i++, $j++) {
  2716. $v_checksum += ord(substr($v_binary_data_last, $j, 1));
  2717. }
  2718. // ----- Write the first 148 bytes of the header in the archive
  2719. $this->_writeBlock($v_binary_data_first, 148);
  2720. // ----- Write the calculated checksum
  2721. $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum));
  2722. $v_binary_data = pack("a8", $v_checksum);
  2723. $this->_writeBlock($v_binary_data, 8);
  2724. // ----- Write the last 356 bytes of the header in the archive
  2725. $this->_writeBlock($v_binary_data_last, 356);
  2726. // ----- Write the filename as content of the block
  2727. $i = 0;
  2728. while (($v_buffer = substr($p_filename, (($i++) * 512), 512)) != '') {
  2729. $v_binary_data = pack("a512", "$v_buffer");
  2730. $this->_writeBlock($v_binary_data);
  2731. }
  2732. return true;
  2733. }
  2734. /**
  2735. * @param mixed $v_binary_data
  2736. * @param mixed $v_header
  2737. * @return bool
  2738. */
  2739. public function _readHeader($v_binary_data, &$v_header)
  2740. {
  2741. if (strlen($v_binary_data) == 0) {
  2742. $v_header['filename'] = '';
  2743. return true;
  2744. }
  2745. if (strlen($v_binary_data) != 512) {
  2746. $v_header['filename'] = '';
  2747. $this->_error('Invalid block size : ' . strlen($v_binary_data));
  2748. return false;
  2749. }
  2750. if (!is_array($v_header)) {
  2751. $v_header = array();
  2752. }
  2753. // ----- Calculate the checksum
  2754. $v_checksum = 0;
  2755. // ..... First part of the header
  2756. $v_binary_split = str_split($v_binary_data);
  2757. $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 0, 148)));
  2758. $v_checksum += array_sum(array_map('ord', array(' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',)));
  2759. $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 156, 512)));
  2760. $v_data = unpack($this->_fmt, $v_binary_data);
  2761. if (strlen($v_data["prefix"]) > 0) {
  2762. $v_data["filename"] = "$v_data[prefix]/$v_data[filename]";
  2763. }
  2764. // ----- Extract the checksum
  2765. $v_data_checksum = trim($v_data['checksum']);
  2766. if (!preg_match('/^[0-7]*$/', $v_data_checksum)) {
  2767. $this->_error(
  2768. 'Invalid checksum for file "' . $v_data['filename']
  2769. . '" : ' . $v_data_checksum . ' extracted'
  2770. );
  2771. return false;
  2772. }
  2773. $v_header['checksum'] = OctDec($v_data_checksum);
  2774. if ($v_header['checksum'] != $v_checksum) {
  2775. $v_header['filename'] = '';
  2776. // ----- Look for last block (empty block)
  2777. if (($v_checksum == 256) && ($v_header['checksum'] == 0)) {
  2778. return true;
  2779. }
  2780. $this->_error(
  2781. 'Invalid checksum for file "' . $v_data['filename']
  2782. . '" : ' . $v_checksum . ' calculated, '
  2783. . $v_header['checksum'] . ' expected'
  2784. );
  2785. return false;
  2786. }
  2787. // ----- Extract the properties
  2788. $v_header['filename'] = rtrim($v_data['filename'], "\0");
  2789. if ($this->_isMaliciousFilename($v_header['filename'])) {
  2790. $this->_error(
  2791. 'Malicious .tar detected, file "' . $v_header['filename'] .
  2792. '" will not install in desired directory tree'
  2793. );
  2794. return false;
  2795. }
  2796. $v_header['mode'] = OctDec(trim($v_data['mode']));
  2797. $v_header['uid'] = OctDec(trim($v_data['uid']));
  2798. $v_header['gid'] = OctDec(trim($v_data['gid']));
  2799. $v_header['size'] = $this->_tarRecToSize($v_data['size']);
  2800. $v_header['mtime'] = OctDec(trim($v_data['mtime']));
  2801. if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
  2802. $v_header['size'] = 0;
  2803. }
  2804. $v_header['link'] = trim($v_data['link']);
  2805. /* ----- All these fields are removed form the header because
  2806. they do not carry interesting info
  2807. $v_header[magic] = trim($v_data[magic]);
  2808. $v_header[version] = trim($v_data[version]);
  2809. $v_header[uname] = trim($v_data[uname]);
  2810. $v_header[gname] = trim($v_data[gname]);
  2811. $v_header[devmajor] = trim($v_data[devmajor]);
  2812. $v_header[devminor] = trim($v_data[devminor]);
  2813. */
  2814. return true;
  2815. }
  2816. /**
  2817. * Convert Tar record size to actual size
  2818. *
  2819. * @param string $tar_size
  2820. * @return size of tar record in bytes
  2821. */
  2822. private function _tarRecToSize($tar_size)
  2823. {
  2824. /*
  2825. * First byte of size has a special meaning if bit 7 is set.
  2826. *
  2827. * Bit 7 indicates base-256 encoding if set.
  2828. * Bit 6 is the sign bit.
  2829. * Bits 5:0 are most significant value bits.
  2830. */
  2831. $ch = ord($tar_size[0]);
  2832. if ($ch & 0x80) {
  2833. // Full 12-bytes record is required.
  2834. $rec_str = $tar_size . "\x00";
  2835. $size = ($ch & 0x40) ? -1 : 0;
  2836. $size = ($size << 6) | ($ch & 0x3f);
  2837. for ($num_ch = 1; $num_ch < 12; ++$num_ch) {
  2838. $size = ($size * 256) + ord($rec_str[$num_ch]);
  2839. }
  2840. return $size;
  2841. } else {
  2842. return OctDec(trim($tar_size));
  2843. }
  2844. }
  2845. /**
  2846. * Detect and report a malicious file name
  2847. *
  2848. * @param string $file
  2849. *
  2850. * @return bool
  2851. */
  2852. private function _isMaliciousFilename($file)
  2853. {
  2854. if (strpos($file, '://') !== false) {
  2855. return true;
  2856. }
  2857. if (strpos($file, '../') !== false || strpos($file, '..\\') !== false) {
  2858. return true;
  2859. }
  2860. return false;
  2861. }
  2862. /**
  2863. * @param $v_header
  2864. * @return bool
  2865. */
  2866. public function _readLongHeader(&$v_header)
  2867. {
  2868. $v_filename = '';
  2869. $v_filesize = $v_header['size'];
  2870. $n = floor($v_header['size'] / 512);
  2871. for ($i = 0; $i < $n; $i++) {
  2872. $v_content = $this->_readBlock();
  2873. $v_filename .= $v_content;
  2874. }
  2875. if (($v_header['size'] % 512) != 0) {
  2876. $v_content = $this->_readBlock();
  2877. $v_filename .= $v_content;
  2878. }
  2879. // ----- Read the next header
  2880. $v_binary_data = $this->_readBlock();
  2881. if (!$this->_readHeader($v_binary_data, $v_header)) {
  2882. return false;
  2883. }
  2884. $v_filename = rtrim(substr($v_filename, 0, $v_filesize), "\0");
  2885. $v_header['filename'] = $v_filename;
  2886. if ($this->_isMaliciousFilename($v_filename)) {
  2887. $this->_error(
  2888. 'Malicious .tar detected, file "' . $v_filename .
  2889. '" will not install in desired directory tree'
  2890. );
  2891. return false;
  2892. }
  2893. return true;
  2894. }
  2895. /**
  2896. * This method extract from the archive one file identified by $p_filename.
  2897. * The return value is a string with the file content, or null on error.
  2898. *
  2899. * @param string $p_filename The path of the file to extract in a string.
  2900. *
  2901. * @return a string with the file content or null.
  2902. */
  2903. private function _extractInString($p_filename)
  2904. {
  2905. $v_result_str = "";
  2906. while (strlen($v_binary_data = $this->_readBlock()) != 0) {
  2907. if (!$this->_readHeader($v_binary_data, $v_header)) {
  2908. return null;
  2909. }
  2910. if ($v_header['filename'] == '') {
  2911. continue;
  2912. }
  2913. switch ($v_header['typeflag']) {
  2914. case 'L':
  2915. {
  2916. if (!$this->_readLongHeader($v_header)) {
  2917. return null;
  2918. }
  2919. }
  2920. break;
  2921. case 'K':
  2922. {
  2923. $v_link_header = $v_header;
  2924. if (!$this->_readLongHeader($v_link_header)) {
  2925. return null;
  2926. }
  2927. $v_header['link'] = $v_link_header['filename'];
  2928. }
  2929. break;
  2930. }
  2931. if ($v_header['filename'] == $p_filename) {
  2932. if ($v_header['typeflag'] == "5") {
  2933. $this->_error(
  2934. 'Unable to extract in string a directory '
  2935. . 'entry {' . $v_header['filename'] . '}'
  2936. );
  2937. return null;
  2938. } else {
  2939. $n = floor($v_header['size'] / 512);
  2940. for ($i = 0; $i < $n; $i++) {
  2941. $v_result_str .= $this->_readBlock();
  2942. }
  2943. if (($v_header['size'] % 512) != 0) {
  2944. $v_content = $this->_readBlock();
  2945. $v_result_str .= substr(
  2946. $v_content,
  2947. 0,
  2948. ($v_header['size'] % 512)
  2949. );
  2950. }
  2951. return $v_result_str;
  2952. }
  2953. } else {
  2954. $this->_jumpBlock(ceil(($v_header['size'] / 512)));
  2955. }
  2956. }
  2957. return null;
  2958. }
  2959. /**
  2960. * @param string $p_path
  2961. * @param string $p_list_detail
  2962. * @param string $p_mode
  2963. * @param string $p_file_list
  2964. * @param string $p_remove_path
  2965. * @param bool $p_preserve
  2966. * @param bool $p_symlinks
  2967. * @return bool
  2968. */
  2969. public function _extractList(
  2970. $p_path,
  2971. &$p_list_detail,
  2972. $p_mode,
  2973. $p_file_list,
  2974. $p_remove_path,
  2975. $p_preserve = false,
  2976. $p_symlinks = true
  2977. )
  2978. {
  2979. $v_result = true;
  2980. $v_nb = 0;
  2981. $v_extract_all = true;
  2982. $v_listing = false;
  2983. $p_path = $this->_translateWinPath($p_path, false);
  2984. if ($p_path == '' || (substr($p_path, 0, 1) != '/'
  2985. && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))
  2986. ) {
  2987. $p_path = "./" . $p_path;
  2988. }
  2989. $p_remove_path = $this->_translateWinPath($p_remove_path);
  2990. // ----- Look for path to remove format (should end by /)
  2991. if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/')) {
  2992. $p_remove_path .= '/';
  2993. }
  2994. $p_remove_path_size = strlen($p_remove_path);
  2995. switch ($p_mode) {
  2996. case "complete" :
  2997. $v_extract_all = true;
  2998. $v_listing = false;
  2999. break;
  3000. case "partial" :
  3001. $v_extract_all = false;
  3002. $v_listing = false;
  3003. break;
  3004. case "list" :
  3005. $v_extract_all = false;
  3006. $v_listing = true;
  3007. break;
  3008. default :
  3009. $this->_error('Invalid extract mode (' . $p_mode . ')');
  3010. return false;
  3011. }
  3012. clearstatcache();
  3013. while (strlen($v_binary_data = $this->_readBlock()) != 0) {
  3014. $v_extract_file = false;
  3015. $v_extraction_stopped = 0;
  3016. if (!$this->_readHeader($v_binary_data, $v_header)) {
  3017. return false;
  3018. }
  3019. if ($v_header['filename'] == '') {
  3020. continue;
  3021. }
  3022. switch ($v_header['typeflag']) {
  3023. case 'L':
  3024. {
  3025. if (!$this->_readLongHeader($v_header)) {
  3026. return null;
  3027. }
  3028. }
  3029. break;
  3030. case 'K':
  3031. {
  3032. $v_link_header = $v_header;
  3033. if (!$this->_readLongHeader($v_link_header)) {
  3034. return null;
  3035. }
  3036. $v_header['link'] = $v_link_header['filename'];
  3037. }
  3038. break;
  3039. }
  3040. // ignore extended / pax headers
  3041. if ($v_header['typeflag'] == 'x' || $v_header['typeflag'] == 'g') {
  3042. $this->_jumpBlock(ceil(($v_header['size'] / 512)));
  3043. continue;
  3044. }
  3045. if ((!$v_extract_all) && (is_array($p_file_list))) {
  3046. // ----- By default no unzip if the file is not found
  3047. $v_extract_file = false;
  3048. for ($i = 0; $i < sizeof($p_file_list); $i++) {
  3049. // ----- Look if it is a directory
  3050. if (substr($p_file_list[$i], -1) == '/') {
  3051. // ----- Look if the directory is in the filename path
  3052. if ((strlen($v_header['filename']) > strlen($p_file_list[$i]))
  3053. && (substr($v_header['filename'], 0, strlen($p_file_list[$i]))
  3054. == $p_file_list[$i])
  3055. ) {
  3056. $v_extract_file = true;
  3057. break;
  3058. }
  3059. } // ----- It is a file, so compare the file names
  3060. elseif ($p_file_list[$i] == $v_header['filename']) {
  3061. $v_extract_file = true;
  3062. break;
  3063. }
  3064. }
  3065. } else {
  3066. $v_extract_file = true;
  3067. }
  3068. // ----- Look if this file need to be extracted
  3069. if (($v_extract_file) && (!$v_listing)) {
  3070. if (($p_remove_path != '')
  3071. && (substr($v_header['filename'] . '/', 0, $p_remove_path_size)
  3072. == $p_remove_path)
  3073. ) {
  3074. $v_header['filename'] = substr(
  3075. $v_header['filename'],
  3076. $p_remove_path_size
  3077. );
  3078. if ($v_header['filename'] == '') {
  3079. continue;
  3080. }
  3081. }
  3082. if (($p_path != './') && ($p_path != '/')) {
  3083. while (substr($p_path, -1) == '/') {
  3084. $p_path = substr($p_path, 0, strlen($p_path) - 1);
  3085. }
  3086. if (substr($v_header['filename'], 0, 1) == '/') {
  3087. $v_header['filename'] = $p_path . $v_header['filename'];
  3088. } else {
  3089. $v_header['filename'] = $p_path . '/' . $v_header['filename'];
  3090. }
  3091. }
  3092. if (file_exists($v_header['filename'])) {
  3093. if ((@is_dir($v_header['filename']))
  3094. && ($v_header['typeflag'] == '')
  3095. ) {
  3096. $this->_error(
  3097. 'File ' . $v_header['filename']
  3098. . ' already exists as a directory'
  3099. );
  3100. return false;
  3101. }
  3102. if (($this->_isArchive($v_header['filename']))
  3103. && ($v_header['typeflag'] == "5")
  3104. ) {
  3105. $this->_error(
  3106. 'Directory ' . $v_header['filename']
  3107. . ' already exists as a file'
  3108. );
  3109. return false;
  3110. }
  3111. if (!is_writeable($v_header['filename'])) {
  3112. $this->_error(
  3113. 'File ' . $v_header['filename']
  3114. . ' already exists and is write protected'
  3115. );
  3116. return false;
  3117. }
  3118. if (filemtime($v_header['filename']) > $v_header['mtime']) {
  3119. // To be completed : An error or silent no replace ?
  3120. }
  3121. } // ----- Check the directory availability and create it if necessary
  3122. elseif (($v_result
  3123. = $this->_dirCheck(
  3124. ($v_header['typeflag'] == "5"
  3125. ? $v_header['filename']
  3126. : dirname($v_header['filename']))
  3127. )) != 1
  3128. ) {
  3129. $this->_error('Unable to create path for ' . $v_header['filename']);
  3130. return false;
  3131. }
  3132. if ($v_extract_file) {
  3133. if ($v_header['typeflag'] == "5") {
  3134. if (!@file_exists($v_header['filename'])) {
  3135. if (!@mkdir($v_header['filename'], 0775)) {
  3136. $this->_error(
  3137. 'Unable to create directory {'
  3138. . $v_header['filename'] . '}'
  3139. );
  3140. return false;
  3141. }
  3142. }
  3143. } elseif ($v_header['typeflag'] == "2") {
  3144. if (!$p_symlinks) {
  3145. $this->_warning('Symbolic links are not allowed. '
  3146. . 'Unable to extract {'
  3147. . $v_header['filename'] . '}'
  3148. );
  3149. return false;
  3150. }
  3151. $absolute_link = FALSE;
  3152. $link_depth = 0;
  3153. if (strpos($v_header['link'], "/") === 0 || strpos($v_header['link'], ':') !== FALSE) {
  3154. $absolute_link = TRUE;
  3155. }
  3156. else {
  3157. $s_filename = preg_replace('@^' . preg_quote($p_path) . '@', "", $v_header['filename']);
  3158. $s_linkname = str_replace('\\', '/', $v_header['link']);
  3159. foreach (explode("/", $s_filename) as $dir) {
  3160. if ($dir === "..") {
  3161. $link_depth--;
  3162. } elseif ($dir !== "" && $dir !== "." ) {
  3163. $link_depth++;
  3164. }
  3165. }
  3166. foreach (explode("/", $s_linkname) as $dir){
  3167. if ($link_depth <= 0) {
  3168. break;
  3169. }
  3170. if ($dir === "..") {
  3171. $link_depth--;
  3172. } elseif ($dir !== "" && $dir !== ".") {
  3173. $link_depth++;
  3174. }
  3175. }
  3176. }
  3177. if ($absolute_link || $link_depth <= 0) {
  3178. $this->_error(
  3179. 'Out-of-path file extraction {'
  3180. . $v_header['filename'] . ' --> ' .
  3181. $v_header['link'] . '}'
  3182. );
  3183. return false;
  3184. }
  3185. if (@file_exists($v_header['filename'])) {
  3186. @unlink($v_header['filename']);
  3187. }
  3188. if (!@symlink($v_header['link'], $v_header['filename'])) {
  3189. $this->_error(
  3190. 'Unable to extract symbolic link {'
  3191. . $v_header['filename'] . '}'
  3192. );
  3193. return false;
  3194. }
  3195. } else {
  3196. if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
  3197. $this->_error(
  3198. 'Error while opening {' . $v_header['filename']
  3199. . '} in write binary mode'
  3200. );
  3201. return false;
  3202. } else {
  3203. $n = floor($v_header['size'] / 512);
  3204. for ($i = 0; $i < $n; $i++) {
  3205. $v_content = $this->_readBlock();
  3206. fwrite($v_dest_file, $v_content, 512);
  3207. }
  3208. if (($v_header['size'] % 512) != 0) {
  3209. $v_content = $this->_readBlock();
  3210. fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
  3211. }
  3212. @fclose($v_dest_file);
  3213. if ($p_preserve) {
  3214. @chown($v_header['filename'], $v_header['uid']);
  3215. @chgrp($v_header['filename'], $v_header['gid']);
  3216. }
  3217. // ----- Change the file mode, mtime
  3218. @touch($v_header['filename'], $v_header['mtime']);
  3219. if ($v_header['mode'] & 0111) {
  3220. // make file executable, obey umask
  3221. $mode = fileperms($v_header['filename']) | (~umask() & 0111);
  3222. @chmod($v_header['filename'], $mode);
  3223. }
  3224. }
  3225. // ----- Check the file size
  3226. clearstatcache();
  3227. if (!is_file($v_header['filename'])) {
  3228. $this->_error(
  3229. 'Extracted file ' . $v_header['filename']
  3230. . 'does not exist. Archive may be corrupted.'
  3231. );
  3232. return false;
  3233. }
  3234. $filesize = filesize($v_header['filename']);
  3235. if ($filesize != $v_header['size']) {
  3236. $this->_error(
  3237. 'Extracted file ' . $v_header['filename']
  3238. . ' does not have the correct file size \''
  3239. . $filesize
  3240. . '\' (' . $v_header['size']
  3241. . ' expected). Archive may be corrupted.'
  3242. );
  3243. return false;
  3244. }
  3245. }
  3246. } else {
  3247. $this->_jumpBlock(ceil(($v_header['size'] / 512)));
  3248. }
  3249. } else {
  3250. $this->_jumpBlock(ceil(($v_header['size'] / 512)));
  3251. }
  3252. /* TBC : Seems to be unused ...
  3253. if ($this->_compress)
  3254. $v_end_of_file = @gzeof($this->_file);
  3255. else
  3256. $v_end_of_file = @feof($this->_file);
  3257. */
  3258. if ($v_listing || $v_extract_file || $v_extraction_stopped) {
  3259. // ----- Log extracted files
  3260. if (($v_file_dir = dirname($v_header['filename']))
  3261. == $v_header['filename']
  3262. ) {
  3263. $v_file_dir = '';
  3264. }
  3265. if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == '')) {
  3266. $v_file_dir = '/';
  3267. }
  3268. $p_list_detail[$v_nb++] = $v_header;
  3269. if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) {
  3270. return true;
  3271. }
  3272. }
  3273. }
  3274. return true;
  3275. }
  3276. /**
  3277. * @return bool
  3278. */
  3279. public function _openAppend()
  3280. {
  3281. if (filesize($this->_tarname) == 0) {
  3282. return $this->_openWrite();
  3283. }
  3284. if ($this->_compress) {
  3285. $this->_close();
  3286. if (!@rename($this->_tarname, $this->_tarname . ".tmp")) {
  3287. $this->_error(
  3288. 'Error while renaming \'' . $this->_tarname
  3289. . '\' to temporary file \'' . $this->_tarname
  3290. . '.tmp\''
  3291. );
  3292. return false;
  3293. }
  3294. if ($this->_compress_type == 'gz') {
  3295. $v_temp_tar = @gzopen($this->_tarname . ".tmp", "rb");
  3296. } elseif ($this->_compress_type == 'bz2') {
  3297. $v_temp_tar = @bzopen($this->_tarname . ".tmp", "r");
  3298. } elseif ($this->_compress_type == 'lzma2') {
  3299. $v_temp_tar = @xzopen($this->_tarname . ".tmp", "r");
  3300. }
  3301. if ($v_temp_tar == 0) {
  3302. $this->_error(
  3303. 'Unable to open file \'' . $this->_tarname
  3304. . '.tmp\' in binary read mode'
  3305. );
  3306. @rename($this->_tarname . ".tmp", $this->_tarname);
  3307. return false;
  3308. }
  3309. if (!$this->_openWrite()) {
  3310. @rename($this->_tarname . ".tmp", $this->_tarname);
  3311. return false;
  3312. }
  3313. if ($this->_compress_type == 'gz') {
  3314. $end_blocks = 0;
  3315. while (!@gzeof($v_temp_tar)) {
  3316. $v_buffer = @gzread($v_temp_tar, 512);
  3317. if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
  3318. $end_blocks++;
  3319. // do not copy end blocks, we will re-make them
  3320. // after appending
  3321. continue;
  3322. } elseif ($end_blocks > 0) {
  3323. for ($i = 0; $i < $end_blocks; $i++) {
  3324. $this->_writeBlock(ARCHIVE_TAR_END_BLOCK);
  3325. }
  3326. $end_blocks = 0;
  3327. }
  3328. $v_binary_data = pack("a512", $v_buffer);
  3329. $this->_writeBlock($v_binary_data);
  3330. }
  3331. @gzclose($v_temp_tar);
  3332. } elseif ($this->_compress_type == 'bz2') {
  3333. $end_blocks = 0;
  3334. while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) {
  3335. if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
  3336. $end_blocks++;
  3337. // do not copy end blocks, we will re-make them
  3338. // after appending
  3339. continue;
  3340. } elseif ($end_blocks > 0) {
  3341. for ($i = 0; $i < $end_blocks; $i++) {
  3342. $this->_writeBlock(ARCHIVE_TAR_END_BLOCK);
  3343. }
  3344. $end_blocks = 0;
  3345. }
  3346. $v_binary_data = pack("a512", $v_buffer);
  3347. $this->_writeBlock($v_binary_data);
  3348. }
  3349. @bzclose($v_temp_tar);
  3350. } elseif ($this->_compress_type == 'lzma2') {
  3351. $end_blocks = 0;
  3352. while (strlen($v_buffer = @xzread($v_temp_tar, 512)) > 0) {
  3353. if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
  3354. $end_blocks++;
  3355. // do not copy end blocks, we will re-make them
  3356. // after appending
  3357. continue;
  3358. } elseif ($end_blocks > 0) {
  3359. for ($i = 0; $i < $end_blocks; $i++) {
  3360. $this->_writeBlock(ARCHIVE_TAR_END_BLOCK);
  3361. }
  3362. $end_blocks = 0;
  3363. }
  3364. $v_binary_data = pack("a512", $v_buffer);
  3365. $this->_writeBlock($v_binary_data);
  3366. }
  3367. @xzclose($v_temp_tar);
  3368. }
  3369. if (!@unlink($this->_tarname . ".tmp")) {
  3370. $this->_error(
  3371. 'Error while deleting temporary file \''
  3372. . $this->_tarname . '.tmp\''
  3373. );
  3374. }
  3375. } else {
  3376. // ----- For not compressed tar, just add files before the last
  3377. // one or two 512 bytes block
  3378. if (!$this->_openReadWrite()) {
  3379. return false;
  3380. }
  3381. clearstatcache();
  3382. $v_size = filesize($this->_tarname);
  3383. // We might have zero, one or two end blocks.
  3384. // The standard is two, but we should try to handle
  3385. // other cases.
  3386. fseek($this->_file, $v_size - 1024);
  3387. if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
  3388. fseek($this->_file, $v_size - 1024);
  3389. } elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
  3390. fseek($this->_file, $v_size - 512);
  3391. }
  3392. }
  3393. return true;
  3394. }
  3395. /**
  3396. * @param $p_filelist
  3397. * @param string $p_add_dir
  3398. * @param string $p_remove_dir
  3399. * @return bool
  3400. */
  3401. public function _append($p_filelist, $p_add_dir = '', $p_remove_dir = '')
  3402. {
  3403. if (!$this->_openAppend()) {
  3404. return false;
  3405. }
  3406. if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir)) {
  3407. $this->_writeFooter();
  3408. }
  3409. $this->_close();
  3410. return true;
  3411. }
  3412. /**
  3413. * Check if a directory exists and create it (including parent
  3414. * dirs) if not.
  3415. *
  3416. * @param string $p_dir directory to check
  3417. *
  3418. * @return bool true if the directory exists or was created
  3419. */
  3420. public function _dirCheck($p_dir)
  3421. {
  3422. clearstatcache();
  3423. if ((@is_dir($p_dir)) || ($p_dir == '')) {
  3424. return true;
  3425. }
  3426. $p_parent_dir = dirname($p_dir);
  3427. if (($p_parent_dir != $p_dir) &&
  3428. ($p_parent_dir != '') &&
  3429. (!$this->_dirCheck($p_parent_dir))
  3430. ) {
  3431. return false;
  3432. }
  3433. if (!@mkdir($p_dir, 0775)) {
  3434. $this->_error("Unable to create directory '$p_dir'");
  3435. return false;
  3436. }
  3437. return true;
  3438. }
  3439. /**
  3440. * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar",
  3441. * rand emove double slashes.
  3442. *
  3443. * @param string $p_dir path to reduce
  3444. *
  3445. * @return string reduced path
  3446. */
  3447. private function _pathReduction($p_dir)
  3448. {
  3449. $v_result = '';
  3450. // ----- Look for not empty path
  3451. if ($p_dir != '') {
  3452. // ----- Explode path by directory names
  3453. $v_list = explode('/', $p_dir);
  3454. // ----- Study directories from last to first
  3455. for ($i = sizeof($v_list) - 1; $i >= 0; $i--) {
  3456. // ----- Look for current path
  3457. if ($v_list[$i] == ".") {
  3458. // ----- Ignore this directory
  3459. // Should be the first $i=0, but no check is done
  3460. } else {
  3461. if ($v_list[$i] == "..") {
  3462. // ----- Ignore it and ignore the $i-1
  3463. $i--;
  3464. } else {
  3465. if (($v_list[$i] == '')
  3466. && ($i != (sizeof($v_list) - 1))
  3467. && ($i != 0)
  3468. ) {
  3469. // ----- Ignore only the double '//' in path,
  3470. // but not the first and last /
  3471. } else {
  3472. $v_result = $v_list[$i] . ($i != (sizeof($v_list) - 1) ? '/'
  3473. . $v_result : '');
  3474. }
  3475. }
  3476. }
  3477. }
  3478. }
  3479. if (defined('OS_WINDOWS') && OS_WINDOWS) {
  3480. $v_result = strtr($v_result, '\\', '/');
  3481. }
  3482. return $v_result;
  3483. }
  3484. /**
  3485. * @param $p_path
  3486. * @param bool $p_remove_disk_letter
  3487. * @return string
  3488. */
  3489. public function _translateWinPath($p_path, $p_remove_disk_letter = true)
  3490. {
  3491. if (defined('OS_WINDOWS') && OS_WINDOWS) {
  3492. // ----- Look for potential disk letter
  3493. if (($p_remove_disk_letter)
  3494. && (($v_position = strpos($p_path, ':')) != false)
  3495. ) {
  3496. $p_path = substr($p_path, $v_position + 1);
  3497. }
  3498. // ----- Change potential windows directory separator
  3499. if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0, 1) == '\\')) {
  3500. $p_path = strtr($p_path, '\\', '/');
  3501. }
  3502. }
  3503. return $p_path;
  3504. }
  3505. }
  3506. <?php
  3507. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3508. /**
  3509. * PHP Version 5
  3510. *
  3511. * Copyright (c) 2001-2015, The PEAR developers
  3512. *
  3513. * This source file is subject to the BSD-2-Clause license,
  3514. * that is bundled with this package in the file LICENSE, and is
  3515. * available through the world-wide-web at the following url:
  3516. * http://opensource.org/licenses/bsd-license.php.
  3517. *
  3518. * @category Console
  3519. * @package Console_Getopt
  3520. * @author Andrei Zmievski <andrei@php.net>
  3521. * @license http://opensource.org/licenses/bsd-license.php BSD-2-Clause
  3522. * @version CVS: $Id$
  3523. * @link http://pear.php.net/package/Console_Getopt
  3524. */
  3525. require_once 'phar://go-pear.phar/' . 'PEAR.php';
  3526. /**
  3527. * Command-line options parsing class.
  3528. *
  3529. * @category Console
  3530. * @package Console_Getopt
  3531. * @author Andrei Zmievski <andrei@php.net>
  3532. * @license http://opensource.org/licenses/bsd-license.php BSD-2-Clause
  3533. * @link http://pear.php.net/package/Console_Getopt
  3534. */
  3535. class Console_Getopt
  3536. {
  3537. /**
  3538. * Parses the command-line options.
  3539. *
  3540. * The first parameter to this function should be the list of command-line
  3541. * arguments without the leading reference to the running program.
  3542. *
  3543. * The second parameter is a string of allowed short options. Each of the
  3544. * option letters can be followed by a colon ':' to specify that the option
  3545. * requires an argument, or a double colon '::' to specify that the option
  3546. * takes an optional argument.
  3547. *
  3548. * The third argument is an optional array of allowed long options. The
  3549. * leading '--' should not be included in the option name. Options that
  3550. * require an argument should be followed by '=', and options that take an
  3551. * option argument should be followed by '=='.
  3552. *
  3553. * The return value is an array of two elements: the list of parsed
  3554. * options and the list of non-option command-line arguments. Each entry in
  3555. * the list of parsed options is a pair of elements - the first one
  3556. * specifies the option, and the second one specifies the option argument,
  3557. * if there was one.
  3558. *
  3559. * Long and short options can be mixed.
  3560. *
  3561. * Most of the semantics of this function are based on GNU getopt_long().
  3562. *
  3563. * @param array $args an array of command-line arguments
  3564. * @param string $short_options specifies the list of allowed short options
  3565. * @param array $long_options specifies the list of allowed long options
  3566. * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
  3567. *
  3568. * @return array two-element array containing the list of parsed options and
  3569. * the non-option arguments
  3570. */
  3571. public static function getopt2($args, $short_options, $long_options = null, $skip_unknown = false)
  3572. {
  3573. return Console_Getopt::doGetopt(2, $args, $short_options, $long_options, $skip_unknown);
  3574. }
  3575. /**
  3576. * This function expects $args to start with the script name (POSIX-style).
  3577. * Preserved for backwards compatibility.
  3578. *
  3579. * @param array $args an array of command-line arguments
  3580. * @param string $short_options specifies the list of allowed short options
  3581. * @param array $long_options specifies the list of allowed long options
  3582. *
  3583. * @see getopt2()
  3584. * @return array two-element array containing the list of parsed options and
  3585. * the non-option arguments
  3586. */
  3587. public static function getopt($args, $short_options, $long_options = null, $skip_unknown = false)
  3588. {
  3589. return Console_Getopt::doGetopt(1, $args, $short_options, $long_options, $skip_unknown);
  3590. }
  3591. /**
  3592. * The actual implementation of the argument parsing code.
  3593. *
  3594. * @param int $version Version to use
  3595. * @param array $args an array of command-line arguments
  3596. * @param string $short_options specifies the list of allowed short options
  3597. * @param array $long_options specifies the list of allowed long options
  3598. * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
  3599. *
  3600. * @return array
  3601. */
  3602. public static function doGetopt($version, $args, $short_options, $long_options = null, $skip_unknown = false)
  3603. {
  3604. // in case you pass directly readPHPArgv() as the first arg
  3605. if (PEAR::isError($args)) {
  3606. return $args;
  3607. }
  3608. if (empty($args)) {
  3609. return array(array(), array());
  3610. }
  3611. $non_opts = $opts = array();
  3612. settype($args, 'array');
  3613. if ($long_options) {
  3614. sort($long_options);
  3615. }
  3616. /*
  3617. * Preserve backwards compatibility with callers that relied on
  3618. * erroneous POSIX fix.
  3619. */
  3620. if ($version < 2) {
  3621. if (isset($args[0][0]) && $args[0][0] != '-') {
  3622. array_shift($args);
  3623. }
  3624. }
  3625. for ($i = 0; $i < count($args); $i++) {
  3626. $arg = $args[$i];
  3627. /* The special element '--' means explicit end of
  3628. options. Treat the rest of the arguments as non-options
  3629. and end the loop. */
  3630. if ($arg == '--') {
  3631. $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
  3632. break;
  3633. }
  3634. if ($arg[0] != '-' || (strlen($arg) > 1 && $arg[1] == '-' && !$long_options)) {
  3635. $non_opts = array_merge($non_opts, array_slice($args, $i));
  3636. break;
  3637. } elseif (strlen($arg) > 1 && $arg[1] == '-') {
  3638. $error = Console_Getopt::_parseLongOption(substr($arg, 2),
  3639. $long_options,
  3640. $opts,
  3641. $i,
  3642. $args,
  3643. $skip_unknown);
  3644. if (PEAR::isError($error)) {
  3645. return $error;
  3646. }
  3647. } elseif ($arg == '-') {
  3648. // - is stdin
  3649. $non_opts = array_merge($non_opts, array_slice($args, $i));
  3650. break;
  3651. } else {
  3652. $error = Console_Getopt::_parseShortOption(substr($arg, 1),
  3653. $short_options,
  3654. $opts,
  3655. $i,
  3656. $args,
  3657. $skip_unknown);
  3658. if (PEAR::isError($error)) {
  3659. return $error;
  3660. }
  3661. }
  3662. }
  3663. return array($opts, $non_opts);
  3664. }
  3665. /**
  3666. * Parse short option
  3667. *
  3668. * @param string $arg Argument
  3669. * @param string[] $short_options Available short options
  3670. * @param string[][] &$opts
  3671. * @param int &$argIdx
  3672. * @param string[] $args
  3673. * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
  3674. *
  3675. * @return void
  3676. */
  3677. protected static function _parseShortOption($arg, $short_options, &$opts, &$argIdx, $args, $skip_unknown)
  3678. {
  3679. for ($i = 0; $i < strlen($arg); $i++) {
  3680. $opt = $arg[$i];
  3681. $opt_arg = null;
  3682. /* Try to find the short option in the specifier string. */
  3683. if (($spec = strstr($short_options, $opt)) === false || $arg[$i] == ':') {
  3684. if ($skip_unknown === true) {
  3685. break;
  3686. }
  3687. $msg = "Console_Getopt: unrecognized option -- $opt";
  3688. return PEAR::raiseError($msg);
  3689. }
  3690. if (strlen($spec) > 1 && $spec[1] == ':') {
  3691. if (strlen($spec) > 2 && $spec[2] == ':') {
  3692. if ($i + 1 < strlen($arg)) {
  3693. /* Option takes an optional argument. Use the remainder of
  3694. the arg string if there is anything left. */
  3695. $opts[] = array($opt, substr($arg, $i + 1));
  3696. break;
  3697. }
  3698. } else {
  3699. /* Option requires an argument. Use the remainder of the arg
  3700. string if there is anything left. */
  3701. if ($i + 1 < strlen($arg)) {
  3702. $opts[] = array($opt, substr($arg, $i + 1));
  3703. break;
  3704. } else if (isset($args[++$argIdx])) {
  3705. $opt_arg = $args[$argIdx];
  3706. /* Else use the next argument. */;
  3707. if (Console_Getopt::_isShortOpt($opt_arg)
  3708. || Console_Getopt::_isLongOpt($opt_arg)) {
  3709. $msg = "option requires an argument --$opt";
  3710. return PEAR::raiseError("Console_Getopt: " . $msg);
  3711. }
  3712. } else {
  3713. $msg = "option requires an argument --$opt";
  3714. return PEAR::raiseError("Console_Getopt: " . $msg);
  3715. }
  3716. }
  3717. }
  3718. $opts[] = array($opt, $opt_arg);
  3719. }
  3720. }
  3721. /**
  3722. * Checks if an argument is a short option
  3723. *
  3724. * @param string $arg Argument to check
  3725. *
  3726. * @return bool
  3727. */
  3728. protected static function _isShortOpt($arg)
  3729. {
  3730. return strlen($arg) == 2 && $arg[0] == '-'
  3731. && preg_match('/[a-zA-Z]/', $arg[1]);
  3732. }
  3733. /**
  3734. * Checks if an argument is a long option
  3735. *
  3736. * @param string $arg Argument to check
  3737. *
  3738. * @return bool
  3739. */
  3740. protected static function _isLongOpt($arg)
  3741. {
  3742. return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' &&
  3743. preg_match('/[a-zA-Z]+$/', substr($arg, 2));
  3744. }
  3745. /**
  3746. * Parse long option
  3747. *
  3748. * @param string $arg Argument
  3749. * @param string[] $long_options Available long options
  3750. * @param string[][] &$opts
  3751. * @param int &$argIdx
  3752. * @param string[] $args
  3753. *
  3754. * @return void|PEAR_Error
  3755. */
  3756. protected static function _parseLongOption($arg, $long_options, &$opts, &$argIdx, $args, $skip_unknown)
  3757. {
  3758. @list($opt, $opt_arg) = explode('=', $arg, 2);
  3759. $opt_len = strlen($opt);
  3760. for ($i = 0; $i < count($long_options); $i++) {
  3761. $long_opt = $long_options[$i];
  3762. $opt_start = substr($long_opt, 0, $opt_len);
  3763. $long_opt_name = str_replace('=', '', $long_opt);
  3764. /* Option doesn't match. Go on to the next one. */
  3765. if ($long_opt_name != $opt) {
  3766. continue;
  3767. }
  3768. $opt_rest = substr($long_opt, $opt_len);
  3769. /* Check that the options uniquely matches one of the allowed
  3770. options. */
  3771. if ($i + 1 < count($long_options)) {
  3772. $next_option_rest = substr($long_options[$i + 1], $opt_len);
  3773. } else {
  3774. $next_option_rest = '';
  3775. }
  3776. if ($opt_rest != '' && $opt[0] != '=' &&
  3777. $i + 1 < count($long_options) &&
  3778. $opt == substr($long_options[$i+1], 0, $opt_len) &&
  3779. $next_option_rest != '' &&
  3780. $next_option_rest[0] != '=') {
  3781. $msg = "Console_Getopt: option --$opt is ambiguous";
  3782. return PEAR::raiseError($msg);
  3783. }
  3784. if (substr($long_opt, -1) == '=') {
  3785. if (substr($long_opt, -2) != '==') {
  3786. /* Long option requires an argument.
  3787. Take the next argument if one wasn't specified. */;
  3788. if (!strlen($opt_arg)) {
  3789. if (!isset($args[++$argIdx])) {
  3790. $msg = "Console_Getopt: option requires an argument --$opt";
  3791. return PEAR::raiseError($msg);
  3792. }
  3793. $opt_arg = $args[$argIdx];
  3794. }
  3795. if (Console_Getopt::_isShortOpt($opt_arg)
  3796. || Console_Getopt::_isLongOpt($opt_arg)) {
  3797. $msg = "Console_Getopt: option requires an argument --$opt";
  3798. return PEAR::raiseError($msg);
  3799. }
  3800. }
  3801. } else if ($opt_arg) {
  3802. $msg = "Console_Getopt: option --$opt doesn't allow an argument";
  3803. return PEAR::raiseError($msg);
  3804. }
  3805. $opts[] = array('--' . $opt, $opt_arg);
  3806. return;
  3807. }
  3808. if ($skip_unknown === true) {
  3809. return;
  3810. }
  3811. return PEAR::raiseError("Console_Getopt: unrecognized option --$opt");
  3812. }
  3813. /**
  3814. * Safely read the $argv PHP array across different PHP configurations.
  3815. * Will take care on register_globals and register_argc_argv ini directives
  3816. *
  3817. * @return mixed the $argv PHP array or PEAR error if not registered
  3818. */
  3819. public static function readPHPArgv()
  3820. {
  3821. global $argv;
  3822. if (!is_array($argv)) {
  3823. if (!@is_array($_SERVER['argv'])) {
  3824. if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
  3825. $msg = "Could not read cmd args (register_argc_argv=Off?)";
  3826. return PEAR::raiseError("Console_Getopt: " . $msg);
  3827. }
  3828. return $GLOBALS['HTTP_SERVER_VARS']['argv'];
  3829. }
  3830. return $_SERVER['argv'];
  3831. }
  3832. return $argv;
  3833. }
  3834. }
  3835. <?php
  3836. require_once 'phar://go-pear.phar/PEAR/Start/CLI.php';
  3837. PEAR::setErrorHandling(PEAR_ERROR_DIE);
  3838. $a = new PEAR_Start_CLI;
  3839. $a->run();
  3840. ?><?php
  3841. /**
  3842. * The OS_Guess class
  3843. *
  3844. * PHP versions 4 and 5
  3845. *
  3846. * @category pear
  3847. * @package PEAR
  3848. * @author Stig Bakken <ssb@php.net>
  3849. * @author Gregory Beaver <cellog@php.net>
  3850. * @copyright 1997-2009 The Authors
  3851. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  3852. * @link http://pear.php.net/package/PEAR
  3853. * @since File available since PEAR 0.1
  3854. */
  3855. // {{{ uname examples
  3856. // php_uname() without args returns the same as 'uname -a', or a PHP-custom
  3857. // string for Windows.
  3858. // PHP versions prior to 4.3 return the uname of the host where PHP was built,
  3859. // as of 4.3 it returns the uname of the host running the PHP code.
  3860. //
  3861. // PC RedHat Linux 7.1:
  3862. // Linux host.example.com 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686 unknown
  3863. //
  3864. // PC Debian Potato:
  3865. // Linux host 2.4.17 #2 SMP Tue Feb 12 15:10:04 CET 2002 i686 unknown
  3866. //
  3867. // PC FreeBSD 3.3:
  3868. // FreeBSD host.example.com 3.3-STABLE FreeBSD 3.3-STABLE #0: Mon Feb 21 00:42:31 CET 2000 root@example.com:/usr/src/sys/compile/CONFIG i386
  3869. //
  3870. // PC FreeBSD 4.3:
  3871. // FreeBSD host.example.com 4.3-RELEASE FreeBSD 4.3-RELEASE #1: Mon Jun 25 11:19:43 EDT 2001 root@example.com:/usr/src/sys/compile/CONFIG i386
  3872. //
  3873. // PC FreeBSD 4.5:
  3874. // FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb 6 23:59:23 CET 2002 root@example.com:/usr/src/sys/compile/CONFIG i386
  3875. //
  3876. // PC FreeBSD 4.5 w/uname from GNU shellutils:
  3877. // FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb i386 unknown
  3878. //
  3879. // HP 9000/712 HP-UX 10:
  3880. // HP-UX iq B.10.10 A 9000/712 2008429113 two-user license
  3881. //
  3882. // HP 9000/712 HP-UX 10 w/uname from GNU shellutils:
  3883. // HP-UX host B.10.10 A 9000/712 unknown
  3884. //
  3885. // IBM RS6000/550 AIX 4.3:
  3886. // AIX host 3 4 000003531C00
  3887. //
  3888. // AIX 4.3 w/uname from GNU shellutils:
  3889. // AIX host 3 4 000003531C00 unknown
  3890. //
  3891. // SGI Onyx IRIX 6.5 w/uname from GNU shellutils:
  3892. // IRIX64 host 6.5 01091820 IP19 mips
  3893. //
  3894. // SGI Onyx IRIX 6.5:
  3895. // IRIX64 host 6.5 01091820 IP19
  3896. //
  3897. // SparcStation 20 Solaris 8 w/uname from GNU shellutils:
  3898. // SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc
  3899. //
  3900. // SparcStation 20 Solaris 8:
  3901. // SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc SUNW,SPARCstation-20
  3902. //
  3903. // Mac OS X (Darwin)
  3904. // Darwin home-eden.local 7.5.0 Darwin Kernel Version 7.5.0: Thu Aug 5 19:26:16 PDT 2004; root:xnu/xnu-517.7.21.obj~3/RELEASE_PPC Power Macintosh
  3905. //
  3906. // Mac OS X early versions
  3907. //
  3908. // }}}
  3909. /* TODO:
  3910. * - define endianness, to allow matchSignature("bigend") etc.
  3911. */
  3912. /**
  3913. * Retrieves information about the current operating system
  3914. *
  3915. * This class uses php_uname() to grok information about the current OS
  3916. *
  3917. * @category pear
  3918. * @package PEAR
  3919. * @author Stig Bakken <ssb@php.net>
  3920. * @author Gregory Beaver <cellog@php.net>
  3921. * @copyright 1997-2020 The Authors
  3922. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  3923. * @version Release: 1.10.16
  3924. * @link http://pear.php.net/package/PEAR
  3925. * @since Class available since Release 0.1
  3926. */
  3927. class OS_Guess
  3928. {
  3929. var $sysname;
  3930. var $nodename;
  3931. var $cpu;
  3932. var $release;
  3933. var $extra;
  3934. function __construct($uname = null)
  3935. {
  3936. list($this->sysname,
  3937. $this->release,
  3938. $this->cpu,
  3939. $this->extra,
  3940. $this->nodename) = $this->parseSignature($uname);
  3941. }
  3942. function parseSignature($uname = null)
  3943. {
  3944. static $sysmap = array(
  3945. 'HP-UX' => 'hpux',
  3946. 'IRIX64' => 'irix',
  3947. );
  3948. static $cpumap = array(
  3949. 'i586' => 'i386',
  3950. 'i686' => 'i386',
  3951. 'ppc' => 'powerpc',
  3952. );
  3953. if ($uname === null) {
  3954. $uname = php_uname();
  3955. }
  3956. $parts = preg_split('/\s+/', trim($uname));
  3957. $n = count($parts);
  3958. $release = $machine = $cpu = '';
  3959. $sysname = $parts[0];
  3960. $nodename = $parts[1];
  3961. $cpu = $parts[$n-1];
  3962. $extra = '';
  3963. if ($cpu == 'unknown') {
  3964. $cpu = $parts[$n - 2];
  3965. }
  3966. switch ($sysname) {
  3967. case 'AIX' :
  3968. $release = "$parts[3].$parts[2]";
  3969. break;
  3970. case 'Windows' :
  3971. $release = $parts[1];
  3972. if ($release == '95/98') {
  3973. $release = '9x';
  3974. }
  3975. $cpu = 'i386';
  3976. break;
  3977. case 'Linux' :
  3978. $extra = $this->_detectGlibcVersion();
  3979. // use only the first two digits from the kernel version
  3980. $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]);
  3981. break;
  3982. case 'Mac' :
  3983. $sysname = 'darwin';
  3984. $nodename = $parts[2];
  3985. $release = $parts[3];
  3986. $cpu = $this->_determineIfPowerpc($cpu, $parts);
  3987. break;
  3988. case 'Darwin' :
  3989. $cpu = $this->_determineIfPowerpc($cpu, $parts);
  3990. $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]);
  3991. break;
  3992. default:
  3993. $release = preg_replace('/-.*/', '', $parts[2]);
  3994. break;
  3995. }
  3996. if (isset($sysmap[$sysname])) {
  3997. $sysname = $sysmap[$sysname];
  3998. } else {
  3999. $sysname = strtolower($sysname);
  4000. }
  4001. if (isset($cpumap[$cpu])) {
  4002. $cpu = $cpumap[$cpu];
  4003. }
  4004. return array($sysname, $release, $cpu, $extra, $nodename);
  4005. }
  4006. function _determineIfPowerpc($cpu, $parts)
  4007. {
  4008. $n = count($parts);
  4009. if ($cpu == 'Macintosh' && $parts[$n - 2] == 'Power') {
  4010. $cpu = 'powerpc';
  4011. }
  4012. return $cpu;
  4013. }
  4014. function _detectGlibcVersion()
  4015. {
  4016. static $glibc = false;
  4017. if ($glibc !== false) {
  4018. return $glibc; // no need to run this multiple times
  4019. }
  4020. $major = $minor = 0;
  4021. include_once 'phar://go-pear.phar/' . "System.php";
  4022. // Let's try reading possible libc.so.6 symlinks
  4023. $libcs = array(
  4024. '/lib64/libc.so.6',
  4025. '/lib/libc.so.6',
  4026. '/lib/i386-linux-gnu/libc.so.6'
  4027. );
  4028. $versions = array();
  4029. foreach ($libcs as $file) {
  4030. $versions = $this->_readGlibCVersionFromSymlink($file);
  4031. if ($versions != []) {
  4032. list($major, $minor) = $versions;
  4033. break;
  4034. }
  4035. }
  4036. // Use glibc's <features.h> header file to
  4037. // get major and minor version number:
  4038. if (!($major && $minor)) {
  4039. $versions = $this->_readGlibCVersionFromFeaturesHeaderFile();
  4040. }
  4041. if (is_array($versions) && $versions != []) {
  4042. list($major, $minor) = $versions;
  4043. }
  4044. if (!($major && $minor)) {
  4045. return $glibc = '';
  4046. }
  4047. return $glibc = "glibc{$major}.{$minor}";
  4048. }
  4049. function _readGlibCVersionFromSymlink($file)
  4050. {
  4051. $versions = array();
  4052. if (@is_link($file)
  4053. && (preg_match('/^libc-(.*)\.so$/', basename(readlink($file)), $matches))
  4054. ) {
  4055. $versions = explode('.', $matches[1]);
  4056. }
  4057. return $versions;
  4058. }
  4059. function _readGlibCVersionFromFeaturesHeaderFile()
  4060. {
  4061. $features_header_file = '/usr/include/features.h';
  4062. if (!(@file_exists($features_header_file)
  4063. && @is_readable($features_header_file))
  4064. ) {
  4065. return array();
  4066. }
  4067. if (!@file_exists('/usr/bin/cpp') || !@is_executable('/usr/bin/cpp')) {
  4068. return $this->_parseFeaturesHeaderFile($features_header_file);
  4069. } // no cpp
  4070. return $this->_fromGlibCTest();
  4071. }
  4072. function _parseFeaturesHeaderFile($features_header_file)
  4073. {
  4074. $features_file = fopen($features_header_file, 'rb');
  4075. while (!feof($features_file)) {
  4076. $line = fgets($features_file, 8192);
  4077. if (!$this->_IsADefinition($line)) {
  4078. continue;
  4079. }
  4080. if (strpos($line, '__GLIBC__')) {
  4081. // major version number #define __GLIBC__ version
  4082. $line = preg_split('/\s+/', $line);
  4083. $glibc_major = trim($line[2]);
  4084. if (isset($glibc_minor)) {
  4085. break;
  4086. }
  4087. continue;
  4088. }
  4089. if (strpos($line, '__GLIBC_MINOR__')) {
  4090. // got the minor version number
  4091. // #define __GLIBC_MINOR__ version
  4092. $line = preg_split('/\s+/', $line);
  4093. $glibc_minor = trim($line[2]);
  4094. if (isset($glibc_major)) {
  4095. break;
  4096. }
  4097. }
  4098. }
  4099. fclose($features_file);
  4100. if (!isset($glibc_major) || !isset($glibc_minor)) {
  4101. return array();
  4102. }
  4103. return array(trim($glibc_major), trim($glibc_minor));
  4104. }
  4105. function _IsADefinition($line)
  4106. {
  4107. if ($line === false) {
  4108. return false;
  4109. }
  4110. return strpos(trim($line), '#define') !== false;
  4111. }
  4112. function _fromGlibCTest()
  4113. {
  4114. $major = null;
  4115. $minor = null;
  4116. $tmpfile = System::mktemp("glibctest");
  4117. $fp = fopen($tmpfile, "w");
  4118. fwrite($fp, "#include <features.h>\n__GLIBC__ __GLIBC_MINOR__\n");
  4119. fclose($fp);
  4120. $cpp = popen("/usr/bin/cpp $tmpfile", "r");
  4121. while ($line = fgets($cpp, 1024)) {
  4122. if ($line[0] == '#' || trim($line) == '') {
  4123. continue;
  4124. }
  4125. if (list($major, $minor) = explode(' ', trim($line))) {
  4126. break;
  4127. }
  4128. }
  4129. pclose($cpp);
  4130. unlink($tmpfile);
  4131. if ($major !== null && $minor !== null) {
  4132. return [$major, $minor];
  4133. }
  4134. }
  4135. function getSignature()
  4136. {
  4137. if (empty($this->extra)) {
  4138. return "{$this->sysname}-{$this->release}-{$this->cpu}";
  4139. }
  4140. return "{$this->sysname}-{$this->release}-{$this->cpu}-{$this->extra}";
  4141. }
  4142. function getSysname()
  4143. {
  4144. return $this->sysname;
  4145. }
  4146. function getNodename()
  4147. {
  4148. return $this->nodename;
  4149. }
  4150. function getCpu()
  4151. {
  4152. return $this->cpu;
  4153. }
  4154. function getRelease()
  4155. {
  4156. return $this->release;
  4157. }
  4158. function getExtra()
  4159. {
  4160. return $this->extra;
  4161. }
  4162. function matchSignature($match)
  4163. {
  4164. $fragments = is_array($match) ? $match : explode('-', $match);
  4165. $n = count($fragments);
  4166. $matches = 0;
  4167. if ($n > 0) {
  4168. $matches += $this->_matchFragment($fragments[0], $this->sysname);
  4169. }
  4170. if ($n > 1) {
  4171. $matches += $this->_matchFragment($fragments[1], $this->release);
  4172. }
  4173. if ($n > 2) {
  4174. $matches += $this->_matchFragment($fragments[2], $this->cpu);
  4175. }
  4176. if ($n > 3) {
  4177. $matches += $this->_matchFragment($fragments[3], $this->extra);
  4178. }
  4179. return ($matches == $n);
  4180. }
  4181. function _matchFragment($fragment, $value)
  4182. {
  4183. if (strcspn($fragment, '*?') < strlen($fragment)) {
  4184. $expression = str_replace(
  4185. array('*', '?', '/'),
  4186. array('.*', '.', '\\/'),
  4187. $fragment
  4188. );
  4189. $reg = '/^' . $expression . '\\z/';
  4190. return preg_match($reg, $value);
  4191. }
  4192. return ($fragment == '*' || !strcasecmp($fragment, $value));
  4193. }
  4194. }
  4195. /*
  4196. * Local Variables:
  4197. * indent-tabs-mode: nil
  4198. * c-basic-offset: 4
  4199. * End:
  4200. */
  4201. <?php
  4202. /**
  4203. * PEAR, the PHP Extension and Application Repository
  4204. *
  4205. * PEAR class and PEAR_Error class
  4206. *
  4207. * PHP versions 4 and 5
  4208. *
  4209. * @category pear
  4210. * @package PEAR
  4211. * @author Sterling Hughes <sterling@php.net>
  4212. * @author Stig Bakken <ssb@php.net>
  4213. * @author Tomas V.V.Cox <cox@idecnet.com>
  4214. * @author Greg Beaver <cellog@php.net>
  4215. * @copyright 1997-2010 The Authors
  4216. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  4217. * @link http://pear.php.net/package/PEAR
  4218. * @since File available since Release 0.1
  4219. */
  4220. /**#@+
  4221. * ERROR constants
  4222. */
  4223. define('PEAR_ERROR_RETURN', 1);
  4224. define('PEAR_ERROR_PRINT', 2);
  4225. define('PEAR_ERROR_TRIGGER', 4);
  4226. define('PEAR_ERROR_DIE', 8);
  4227. define('PEAR_ERROR_CALLBACK', 16);
  4228. /**
  4229. * WARNING: obsolete
  4230. * @deprecated
  4231. */
  4232. define('PEAR_ERROR_EXCEPTION', 32);
  4233. /**#@-*/
  4234. if (substr(PHP_OS, 0, 3) == 'WIN') {
  4235. define('OS_WINDOWS', true);
  4236. define('OS_UNIX', false);
  4237. define('PEAR_OS', 'Windows');
  4238. } else {
  4239. define('OS_WINDOWS', false);
  4240. define('OS_UNIX', true);
  4241. define('PEAR_OS', 'Unix'); // blatant assumption
  4242. }
  4243. $GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN;
  4244. $GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE;
  4245. $GLOBALS['_PEAR_destructor_object_list'] = array();
  4246. $GLOBALS['_PEAR_shutdown_funcs'] = array();
  4247. $GLOBALS['_PEAR_error_handler_stack'] = array();
  4248. if(function_exists('ini_set')) {
  4249. @ini_set('track_errors', true);
  4250. }
  4251. /**
  4252. * Base class for other PEAR classes. Provides rudimentary
  4253. * emulation of destructors.
  4254. *
  4255. * If you want a destructor in your class, inherit PEAR and make a
  4256. * destructor method called _yourclassname (same name as the
  4257. * constructor, but with a "_" prefix). Also, in your constructor you
  4258. * have to call the PEAR constructor: $this->PEAR();.
  4259. * The destructor method will be called without parameters. Note that
  4260. * at in some SAPI implementations (such as Apache), any output during
  4261. * the request shutdown (in which destructors are called) seems to be
  4262. * discarded. If you need to get any debug information from your
  4263. * destructor, use error_log(), syslog() or something similar.
  4264. *
  4265. * IMPORTANT! To use the emulated destructors you need to create the
  4266. * objects by reference: $obj =& new PEAR_child;
  4267. *
  4268. * @category pear
  4269. * @package PEAR
  4270. * @author Stig Bakken <ssb@php.net>
  4271. * @author Tomas V.V. Cox <cox@idecnet.com>
  4272. * @author Greg Beaver <cellog@php.net>
  4273. * @copyright 1997-2006 The PHP Group
  4274. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  4275. * @version Release: 1.10.16
  4276. * @link http://pear.php.net/package/PEAR
  4277. * @see PEAR_Error
  4278. * @since Class available since PHP 4.0.2
  4279. * @link http://pear.php.net/manual/en/core.pear.php#core.pear.pear
  4280. */
  4281. class PEAR
  4282. {
  4283. /**
  4284. * Whether to enable internal debug messages.
  4285. *
  4286. * @var bool
  4287. * @access private
  4288. */
  4289. var $_debug = false;
  4290. /**
  4291. * Default error mode for this object.
  4292. *
  4293. * @var int
  4294. * @access private
  4295. */
  4296. var $_default_error_mode = null;
  4297. /**
  4298. * Default error options used for this object when error mode
  4299. * is PEAR_ERROR_TRIGGER.
  4300. *
  4301. * @var int
  4302. * @access private
  4303. */
  4304. var $_default_error_options = null;
  4305. /**
  4306. * Default error handler (callback) for this object, if error mode is
  4307. * PEAR_ERROR_CALLBACK.
  4308. *
  4309. * @var string
  4310. * @access private
  4311. */
  4312. var $_default_error_handler = '';
  4313. /**
  4314. * Which class to use for error objects.
  4315. *
  4316. * @var string
  4317. * @access private
  4318. */
  4319. var $_error_class = 'PEAR_Error';
  4320. /**
  4321. * An array of expected errors.
  4322. *
  4323. * @var array
  4324. * @access private
  4325. */
  4326. var $_expected_errors = array();
  4327. /**
  4328. * List of methods that can be called both statically and non-statically.
  4329. * @var array
  4330. */
  4331. protected static $bivalentMethods = array(
  4332. 'setErrorHandling' => true,
  4333. 'raiseError' => true,
  4334. 'throwError' => true,
  4335. 'pushErrorHandling' => true,
  4336. 'popErrorHandling' => true,
  4337. );
  4338. /**
  4339. * Constructor. Registers this object in
  4340. * $_PEAR_destructor_object_list for destructor emulation if a
  4341. * destructor object exists.
  4342. *
  4343. * @param string $error_class (optional) which class to use for
  4344. * error objects, defaults to PEAR_Error.
  4345. * @access public
  4346. * @return void
  4347. */
  4348. function __construct($error_class = null)
  4349. {
  4350. $classname = strtolower(get_class($this));
  4351. if ($this->_debug) {
  4352. print "PEAR constructor called, class=$classname\n";
  4353. }
  4354. if ($error_class !== null) {
  4355. $this->_error_class = $error_class;
  4356. }
  4357. while ($classname && strcasecmp($classname, "pear")) {
  4358. $destructor = "_$classname";
  4359. if (method_exists($this, $destructor)) {
  4360. global $_PEAR_destructor_object_list;
  4361. $_PEAR_destructor_object_list[] = $this;
  4362. if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
  4363. register_shutdown_function("_PEAR_call_destructors");
  4364. $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
  4365. }
  4366. break;
  4367. } else {
  4368. $classname = get_parent_class($classname);
  4369. }
  4370. }
  4371. }
  4372. /**
  4373. * Only here for backwards compatibility.
  4374. * E.g. Archive_Tar calls $this->PEAR() in its constructor.
  4375. *
  4376. * @param string $error_class Which class to use for error objects,
  4377. * defaults to PEAR_Error.
  4378. */
  4379. public function PEAR($error_class = null)
  4380. {
  4381. self::__construct($error_class);
  4382. }
  4383. /**
  4384. * Destructor (the emulated type of...). Does nothing right now,
  4385. * but is included for forward compatibility, so subclass
  4386. * destructors should always call it.
  4387. *
  4388. * See the note in the class desciption about output from
  4389. * destructors.
  4390. *
  4391. * @access public
  4392. * @return void
  4393. */
  4394. function _PEAR() {
  4395. if ($this->_debug) {
  4396. printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
  4397. }
  4398. }
  4399. public function __call($method, $arguments)
  4400. {
  4401. if (!isset(self::$bivalentMethods[$method])) {
  4402. trigger_error(
  4403. 'Call to undefined method PEAR::' . $method . '()', E_USER_ERROR
  4404. );
  4405. }
  4406. return call_user_func_array(
  4407. array(__CLASS__, '_' . $method),
  4408. array_merge(array($this), $arguments)
  4409. );
  4410. }
  4411. public static function __callStatic($method, $arguments)
  4412. {
  4413. if (!isset(self::$bivalentMethods[$method])) {
  4414. trigger_error(
  4415. 'Call to undefined method PEAR::' . $method . '()', E_USER_ERROR
  4416. );
  4417. }
  4418. return call_user_func_array(
  4419. array(__CLASS__, '_' . $method),
  4420. array_merge(array(null), $arguments)
  4421. );
  4422. }
  4423. /**
  4424. * If you have a class that's mostly/entirely static, and you need static
  4425. * properties, you can use this method to simulate them. Eg. in your method(s)
  4426. * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
  4427. * You MUST use a reference, or they will not persist!
  4428. *
  4429. * @param string $class The calling classname, to prevent clashes
  4430. * @param string $var The variable to retrieve.
  4431. * @return mixed A reference to the variable. If not set it will be
  4432. * auto initialised to NULL.
  4433. */
  4434. public static function &getStaticProperty($class, $var)
  4435. {
  4436. static $properties;
  4437. if (!isset($properties[$class])) {
  4438. $properties[$class] = array();
  4439. }
  4440. if (!array_key_exists($var, $properties[$class])) {
  4441. $properties[$class][$var] = null;
  4442. }
  4443. return $properties[$class][$var];
  4444. }
  4445. /**
  4446. * Use this function to register a shutdown method for static
  4447. * classes.
  4448. *
  4449. * @param mixed $func The function name (or array of class/method) to call
  4450. * @param mixed $args The arguments to pass to the function
  4451. *
  4452. * @return void
  4453. */
  4454. public static function registerShutdownFunc($func, $args = array())
  4455. {
  4456. // if we are called statically, there is a potential
  4457. // that no shutdown func is registered. Bug #6445
  4458. if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
  4459. register_shutdown_function("_PEAR_call_destructors");
  4460. $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
  4461. }
  4462. $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
  4463. }
  4464. /**
  4465. * Tell whether a value is a PEAR error.
  4466. *
  4467. * @param mixed $data the value to test
  4468. * @param int $code if $data is an error object, return true
  4469. * only if $code is a string and
  4470. * $obj->getMessage() == $code or
  4471. * $code is an integer and $obj->getCode() == $code
  4472. *
  4473. * @return bool true if parameter is an error
  4474. */
  4475. public static function isError($data, $code = null)
  4476. {
  4477. if (!is_a($data, 'PEAR_Error')) {
  4478. return false;
  4479. }
  4480. if (is_null($code)) {
  4481. return true;
  4482. } elseif (is_string($code)) {
  4483. return $data->getMessage() == $code;
  4484. }
  4485. return $data->getCode() == $code;
  4486. }
  4487. /**
  4488. * Sets how errors generated by this object should be handled.
  4489. * Can be invoked both in objects and statically. If called
  4490. * statically, setErrorHandling sets the default behaviour for all
  4491. * PEAR objects. If called in an object, setErrorHandling sets
  4492. * the default behaviour for that object.
  4493. *
  4494. * @param object $object
  4495. * Object the method was called on (non-static mode)
  4496. *
  4497. * @param int $mode
  4498. * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
  4499. * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
  4500. * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
  4501. *
  4502. * @param mixed $options
  4503. * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
  4504. * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
  4505. *
  4506. * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
  4507. * to be the callback function or method. A callback
  4508. * function is a string with the name of the function, a
  4509. * callback method is an array of two elements: the element
  4510. * at index 0 is the object, and the element at index 1 is
  4511. * the name of the method to call in the object.
  4512. *
  4513. * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
  4514. * a printf format string used when printing the error
  4515. * message.
  4516. *
  4517. * @access public
  4518. * @return void
  4519. * @see PEAR_ERROR_RETURN
  4520. * @see PEAR_ERROR_PRINT
  4521. * @see PEAR_ERROR_TRIGGER
  4522. * @see PEAR_ERROR_DIE
  4523. * @see PEAR_ERROR_CALLBACK
  4524. * @see PEAR_ERROR_EXCEPTION
  4525. *
  4526. * @since PHP 4.0.5
  4527. */
  4528. protected static function _setErrorHandling(
  4529. $object, $mode = null, $options = null
  4530. ) {
  4531. if ($object !== null) {
  4532. $setmode = &$object->_default_error_mode;
  4533. $setoptions = &$object->_default_error_options;
  4534. } else {
  4535. $setmode = &$GLOBALS['_PEAR_default_error_mode'];
  4536. $setoptions = &$GLOBALS['_PEAR_default_error_options'];
  4537. }
  4538. switch ($mode) {
  4539. case PEAR_ERROR_EXCEPTION:
  4540. case PEAR_ERROR_RETURN:
  4541. case PEAR_ERROR_PRINT:
  4542. case PEAR_ERROR_TRIGGER:
  4543. case PEAR_ERROR_DIE:
  4544. case null:
  4545. $setmode = $mode;
  4546. $setoptions = $options;
  4547. break;
  4548. case PEAR_ERROR_CALLBACK:
  4549. $setmode = $mode;
  4550. // class/object method callback
  4551. if (is_callable($options)) {
  4552. $setoptions = $options;
  4553. } else {
  4554. trigger_error("invalid error callback", E_USER_WARNING);
  4555. }
  4556. break;
  4557. default:
  4558. trigger_error("invalid error mode", E_USER_WARNING);
  4559. break;
  4560. }
  4561. }
  4562. /**
  4563. * This method is used to tell which errors you expect to get.
  4564. * Expected errors are always returned with error mode
  4565. * PEAR_ERROR_RETURN. Expected error codes are stored in a stack,
  4566. * and this method pushes a new element onto it. The list of
  4567. * expected errors are in effect until they are popped off the
  4568. * stack with the popExpect() method.
  4569. *
  4570. * Note that this method can not be called statically
  4571. *
  4572. * @param mixed $code a single error code or an array of error codes to expect
  4573. *
  4574. * @return int the new depth of the "expected errors" stack
  4575. * @access public
  4576. */
  4577. function expectError($code = '*')
  4578. {
  4579. if (is_array($code)) {
  4580. array_push($this->_expected_errors, $code);
  4581. } else {
  4582. array_push($this->_expected_errors, array($code));
  4583. }
  4584. return count($this->_expected_errors);
  4585. }
  4586. /**
  4587. * This method pops one element off the expected error codes
  4588. * stack.
  4589. *
  4590. * @return array the list of error codes that were popped
  4591. */
  4592. function popExpect()
  4593. {
  4594. return array_pop($this->_expected_errors);
  4595. }
  4596. /**
  4597. * This method checks unsets an error code if available
  4598. *
  4599. * @param mixed error code
  4600. * @return bool true if the error code was unset, false otherwise
  4601. * @access private
  4602. * @since PHP 4.3.0
  4603. */
  4604. function _checkDelExpect($error_code)
  4605. {
  4606. $deleted = false;
  4607. foreach ($this->_expected_errors as $key => $error_array) {
  4608. if (in_array($error_code, $error_array)) {
  4609. unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
  4610. $deleted = true;
  4611. }
  4612. // clean up empty arrays
  4613. if (0 == count($this->_expected_errors[$key])) {
  4614. unset($this->_expected_errors[$key]);
  4615. }
  4616. }
  4617. return $deleted;
  4618. }
  4619. /**
  4620. * This method deletes all occurrences of the specified element from
  4621. * the expected error codes stack.
  4622. *
  4623. * @param mixed $error_code error code that should be deleted
  4624. * @return mixed list of error codes that were deleted or error
  4625. * @access public
  4626. * @since PHP 4.3.0
  4627. */
  4628. function delExpect($error_code)
  4629. {
  4630. $deleted = false;
  4631. if ((is_array($error_code) && (0 != count($error_code)))) {
  4632. // $error_code is a non-empty array here; we walk through it trying
  4633. // to unset all values
  4634. foreach ($error_code as $key => $error) {
  4635. $deleted = $this->_checkDelExpect($error) ? true : false;
  4636. }
  4637. return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
  4638. } elseif (!empty($error_code)) {
  4639. // $error_code comes alone, trying to unset it
  4640. if ($this->_checkDelExpect($error_code)) {
  4641. return true;
  4642. }
  4643. return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
  4644. }
  4645. // $error_code is empty
  4646. return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
  4647. }
  4648. /**
  4649. * This method is a wrapper that returns an instance of the
  4650. * configured error class with this object's default error
  4651. * handling applied. If the $mode and $options parameters are not
  4652. * specified, the object's defaults are used.
  4653. *
  4654. * @param mixed $message a text error message or a PEAR error object
  4655. *
  4656. * @param int $code a numeric error code (it is up to your class
  4657. * to define these if you want to use codes)
  4658. *
  4659. * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
  4660. * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
  4661. * PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
  4662. *
  4663. * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
  4664. * specifies the PHP-internal error level (one of
  4665. * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
  4666. * If $mode is PEAR_ERROR_CALLBACK, this
  4667. * parameter specifies the callback function or
  4668. * method. In other error modes this parameter
  4669. * is ignored.
  4670. *
  4671. * @param string $userinfo If you need to pass along for example debug
  4672. * information, this parameter is meant for that.
  4673. *
  4674. * @param string $error_class The returned error object will be
  4675. * instantiated from this class, if specified.
  4676. *
  4677. * @param bool $skipmsg If true, raiseError will only pass error codes,
  4678. * the error message parameter will be dropped.
  4679. *
  4680. * @return object a PEAR error object
  4681. * @see PEAR::setErrorHandling
  4682. * @since PHP 4.0.5
  4683. */
  4684. protected static function _raiseError($object,
  4685. $message = null,
  4686. $code = null,
  4687. $mode = null,
  4688. $options = null,
  4689. $userinfo = null,
  4690. $error_class = null,
  4691. $skipmsg = false)
  4692. {
  4693. // The error is yet a PEAR error object
  4694. if (is_object($message)) {
  4695. $code = $message->getCode();
  4696. $userinfo = $message->getUserInfo();
  4697. $error_class = $message->getType();
  4698. $message->error_message_prefix = '';
  4699. $message = $message->getMessage();
  4700. }
  4701. if (
  4702. $object !== null &&
  4703. isset($object->_expected_errors) &&
  4704. count($object->_expected_errors) > 0 &&
  4705. count($exp = end($object->_expected_errors))
  4706. ) {
  4707. if ($exp[0] === "*" ||
  4708. (is_int(reset($exp)) && in_array($code, $exp)) ||
  4709. (is_string(reset($exp)) && in_array($message, $exp))
  4710. ) {
  4711. $mode = PEAR_ERROR_RETURN;
  4712. }
  4713. }
  4714. // No mode given, try global ones
  4715. if ($mode === null) {
  4716. // Class error handler
  4717. if ($object !== null && isset($object->_default_error_mode)) {
  4718. $mode = $object->_default_error_mode;
  4719. $options = $object->_default_error_options;
  4720. // Global error handler
  4721. } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
  4722. $mode = $GLOBALS['_PEAR_default_error_mode'];
  4723. $options = $GLOBALS['_PEAR_default_error_options'];
  4724. }
  4725. }
  4726. if ($error_class !== null) {
  4727. $ec = $error_class;
  4728. } elseif ($object !== null && isset($object->_error_class)) {
  4729. $ec = $object->_error_class;
  4730. } else {
  4731. $ec = 'PEAR_Error';
  4732. }
  4733. if ($skipmsg) {
  4734. $a = new $ec($code, $mode, $options, $userinfo);
  4735. } else {
  4736. $a = new $ec($message, $code, $mode, $options, $userinfo);
  4737. }
  4738. return $a;
  4739. }
  4740. /**
  4741. * Simpler form of raiseError with fewer options. In most cases
  4742. * message, code and userinfo are enough.
  4743. *
  4744. * @param mixed $message a text error message or a PEAR error object
  4745. *
  4746. * @param int $code a numeric error code (it is up to your class
  4747. * to define these if you want to use codes)
  4748. *
  4749. * @param string $userinfo If you need to pass along for example debug
  4750. * information, this parameter is meant for that.
  4751. *
  4752. * @return object a PEAR error object
  4753. * @see PEAR::raiseError
  4754. */
  4755. protected static function _throwError($object, $message = null, $code = null, $userinfo = null)
  4756. {
  4757. if ($object !== null) {
  4758. $a = $object->raiseError($message, $code, null, null, $userinfo);
  4759. return $a;
  4760. }
  4761. $a = PEAR::raiseError($message, $code, null, null, $userinfo);
  4762. return $a;
  4763. }
  4764. public static function staticPushErrorHandling($mode, $options = null)
  4765. {
  4766. $stack = &$GLOBALS['_PEAR_error_handler_stack'];
  4767. $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
  4768. $def_options = &$GLOBALS['_PEAR_default_error_options'];
  4769. $stack[] = array($def_mode, $def_options);
  4770. switch ($mode) {
  4771. case PEAR_ERROR_EXCEPTION:
  4772. case PEAR_ERROR_RETURN:
  4773. case PEAR_ERROR_PRINT:
  4774. case PEAR_ERROR_TRIGGER:
  4775. case PEAR_ERROR_DIE:
  4776. case null:
  4777. $def_mode = $mode;
  4778. $def_options = $options;
  4779. break;
  4780. case PEAR_ERROR_CALLBACK:
  4781. $def_mode = $mode;
  4782. // class/object method callback
  4783. if (is_callable($options)) {
  4784. $def_options = $options;
  4785. } else {
  4786. trigger_error("invalid error callback", E_USER_WARNING);
  4787. }
  4788. break;
  4789. default:
  4790. trigger_error("invalid error mode", E_USER_WARNING);
  4791. break;
  4792. }
  4793. $stack[] = array($mode, $options);
  4794. return true;
  4795. }
  4796. public static function staticPopErrorHandling()
  4797. {
  4798. $stack = &$GLOBALS['_PEAR_error_handler_stack'];
  4799. $setmode = &$GLOBALS['_PEAR_default_error_mode'];
  4800. $setoptions = &$GLOBALS['_PEAR_default_error_options'];
  4801. array_pop($stack);
  4802. list($mode, $options) = $stack[sizeof($stack) - 1];
  4803. array_pop($stack);
  4804. switch ($mode) {
  4805. case PEAR_ERROR_EXCEPTION:
  4806. case PEAR_ERROR_RETURN:
  4807. case PEAR_ERROR_PRINT:
  4808. case PEAR_ERROR_TRIGGER:
  4809. case PEAR_ERROR_DIE:
  4810. case null:
  4811. $setmode = $mode;
  4812. $setoptions = $options;
  4813. break;
  4814. case PEAR_ERROR_CALLBACK:
  4815. $setmode = $mode;
  4816. // class/object method callback
  4817. if (is_callable($options)) {
  4818. $setoptions = $options;
  4819. } else {
  4820. trigger_error("invalid error callback", E_USER_WARNING);
  4821. }
  4822. break;
  4823. default:
  4824. trigger_error("invalid error mode", E_USER_WARNING);
  4825. break;
  4826. }
  4827. return true;
  4828. }
  4829. /**
  4830. * Push a new error handler on top of the error handler options stack. With this
  4831. * you can easily override the actual error handler for some code and restore
  4832. * it later with popErrorHandling.
  4833. *
  4834. * @param mixed $mode (same as setErrorHandling)
  4835. * @param mixed $options (same as setErrorHandling)
  4836. *
  4837. * @return bool Always true
  4838. *
  4839. * @see PEAR::setErrorHandling
  4840. */
  4841. protected static function _pushErrorHandling($object, $mode, $options = null)
  4842. {
  4843. $stack = &$GLOBALS['_PEAR_error_handler_stack'];
  4844. if ($object !== null) {
  4845. $def_mode = &$object->_default_error_mode;
  4846. $def_options = &$object->_default_error_options;
  4847. } else {
  4848. $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
  4849. $def_options = &$GLOBALS['_PEAR_default_error_options'];
  4850. }
  4851. $stack[] = array($def_mode, $def_options);
  4852. if ($object !== null) {
  4853. $object->setErrorHandling($mode, $options);
  4854. } else {
  4855. PEAR::setErrorHandling($mode, $options);
  4856. }
  4857. $stack[] = array($mode, $options);
  4858. return true;
  4859. }
  4860. /**
  4861. * Pop the last error handler used
  4862. *
  4863. * @return bool Always true
  4864. *
  4865. * @see PEAR::pushErrorHandling
  4866. */
  4867. protected static function _popErrorHandling($object)
  4868. {
  4869. $stack = &$GLOBALS['_PEAR_error_handler_stack'];
  4870. array_pop($stack);
  4871. list($mode, $options) = $stack[sizeof($stack) - 1];
  4872. array_pop($stack);
  4873. if ($object !== null) {
  4874. $object->setErrorHandling($mode, $options);
  4875. } else {
  4876. PEAR::setErrorHandling($mode, $options);
  4877. }
  4878. return true;
  4879. }
  4880. /**
  4881. * OS independent PHP extension load. Remember to take care
  4882. * on the correct extension name for case sensitive OSes.
  4883. *
  4884. * @param string $ext The extension name
  4885. * @return bool Success or not on the dl() call
  4886. */
  4887. public static function loadExtension($ext)
  4888. {
  4889. if (extension_loaded($ext)) {
  4890. return true;
  4891. }
  4892. // if either returns true dl() will produce a FATAL error, stop that
  4893. if (
  4894. function_exists('dl') === false ||
  4895. ini_get('enable_dl') != 1
  4896. ) {
  4897. return false;
  4898. }
  4899. if (OS_WINDOWS) {
  4900. $suffix = '.dll';
  4901. } elseif (PHP_OS == 'HP-UX') {
  4902. $suffix = '.sl';
  4903. } elseif (PHP_OS == 'AIX') {
  4904. $suffix = '.a';
  4905. } elseif (PHP_OS == 'OSX') {
  4906. $suffix = '.bundle';
  4907. } else {
  4908. $suffix = '.so';
  4909. }
  4910. return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
  4911. }
  4912. /**
  4913. * Get SOURCE_DATE_EPOCH environment variable
  4914. * See https://reproducible-builds.org/specs/source-date-epoch/
  4915. *
  4916. * @return int
  4917. * @access public
  4918. */
  4919. static function getSourceDateEpoch()
  4920. {
  4921. if ($source_date_epoch = getenv('SOURCE_DATE_EPOCH')) {
  4922. if (preg_match('/^\d+$/', $source_date_epoch)) {
  4923. return (int) $source_date_epoch;
  4924. } else {
  4925. // "If the value is malformed, the build process SHOULD exit with a non-zero error code."
  4926. self::raiseError("Invalid SOURCE_DATE_EPOCH: $source_date_epoch");
  4927. exit(1);
  4928. }
  4929. } else {
  4930. return time();
  4931. }
  4932. }
  4933. }
  4934. function _PEAR_call_destructors()
  4935. {
  4936. global $_PEAR_destructor_object_list;
  4937. if (is_array($_PEAR_destructor_object_list) &&
  4938. sizeof($_PEAR_destructor_object_list))
  4939. {
  4940. reset($_PEAR_destructor_object_list);
  4941. $destructLifoExists = PEAR::getStaticProperty('PEAR', 'destructlifo');
  4942. if ($destructLifoExists) {
  4943. $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
  4944. }
  4945. foreach ($_PEAR_destructor_object_list as $k => $objref) {
  4946. $classname = get_class($objref);
  4947. while ($classname) {
  4948. $destructor = "_$classname";
  4949. if (method_exists($objref, $destructor)) {
  4950. $objref->$destructor();
  4951. break;
  4952. } else {
  4953. $classname = get_parent_class($classname);
  4954. }
  4955. }
  4956. }
  4957. // Empty the object list to ensure that destructors are
  4958. // not called more than once.
  4959. $_PEAR_destructor_object_list = array();
  4960. }
  4961. // Now call the shutdown functions
  4962. if (
  4963. isset($GLOBALS['_PEAR_shutdown_funcs']) &&
  4964. is_array($GLOBALS['_PEAR_shutdown_funcs']) &&
  4965. !empty($GLOBALS['_PEAR_shutdown_funcs'])
  4966. ) {
  4967. foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
  4968. call_user_func_array($value[0], $value[1]);
  4969. }
  4970. }
  4971. }
  4972. /**
  4973. * Standard PEAR error class for PHP 4
  4974. *
  4975. * This class is supserseded by {@link PEAR_Exception} in PHP 5
  4976. *
  4977. * @category pear
  4978. * @package PEAR
  4979. * @author Stig Bakken <ssb@php.net>
  4980. * @author Tomas V.V. Cox <cox@idecnet.com>
  4981. * @author Gregory Beaver <cellog@php.net>
  4982. * @copyright 1997-2006 The PHP Group
  4983. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  4984. * @version Release: 1.10.16
  4985. * @link http://pear.php.net/manual/en/core.pear.pear-error.php
  4986. * @see PEAR::raiseError(), PEAR::throwError()
  4987. * @since Class available since PHP 4.0.2
  4988. */
  4989. class PEAR_Error
  4990. {
  4991. var $error_message_prefix = '';
  4992. var $mode = PEAR_ERROR_RETURN;
  4993. var $level = E_USER_NOTICE;
  4994. var $code = -1;
  4995. var $message = '';
  4996. var $userinfo = '';
  4997. var $backtrace = null;
  4998. var $callback = null;
  4999. /**
  5000. * PEAR_Error constructor
  5001. *
  5002. * @param string $message message
  5003. *
  5004. * @param int $code (optional) error code
  5005. *
  5006. * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN,
  5007. * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
  5008. * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
  5009. *
  5010. * @param mixed $options (optional) error level, _OR_ in the case of
  5011. * PEAR_ERROR_CALLBACK, the callback function or object/method
  5012. * tuple.
  5013. *
  5014. * @param string $userinfo (optional) additional user/debug info
  5015. *
  5016. * @access public
  5017. *
  5018. */
  5019. function __construct($message = 'unknown error', $code = null,
  5020. $mode = null, $options = null, $userinfo = null)
  5021. {
  5022. if ($mode === null) {
  5023. $mode = PEAR_ERROR_RETURN;
  5024. }
  5025. $this->message = $message;
  5026. $this->code = $code;
  5027. $this->mode = $mode;
  5028. $this->userinfo = $userinfo;
  5029. $skiptrace = PEAR::getStaticProperty('PEAR_Error', 'skiptrace');
  5030. if (!$skiptrace) {
  5031. $this->backtrace = debug_backtrace();
  5032. if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) {
  5033. unset($this->backtrace[0]['object']);
  5034. }
  5035. }
  5036. if ($mode & PEAR_ERROR_CALLBACK) {
  5037. $this->level = E_USER_NOTICE;
  5038. $this->callback = $options;
  5039. } else {
  5040. if ($options === null) {
  5041. $options = E_USER_NOTICE;
  5042. }
  5043. $this->level = $options;
  5044. $this->callback = null;
  5045. }
  5046. if ($this->mode & PEAR_ERROR_PRINT) {
  5047. if (is_null($options) || is_int($options)) {
  5048. $format = "%s";
  5049. } else {
  5050. $format = $options;
  5051. }
  5052. printf($format, $this->getMessage());
  5053. }
  5054. if ($this->mode & PEAR_ERROR_TRIGGER) {
  5055. trigger_error($this->getMessage(), $this->level);
  5056. }
  5057. if ($this->mode & PEAR_ERROR_DIE) {
  5058. $msg = $this->getMessage();
  5059. if (is_null($options) || is_int($options)) {
  5060. $format = "%s";
  5061. if (substr($msg, -1) != "\n") {
  5062. $msg .= "\n";
  5063. }
  5064. } else {
  5065. $format = $options;
  5066. }
  5067. printf($format, $msg);
  5068. exit($code);
  5069. }
  5070. if ($this->mode & PEAR_ERROR_CALLBACK && is_callable($this->callback)) {
  5071. call_user_func($this->callback, $this);
  5072. }
  5073. if ($this->mode & PEAR_ERROR_EXCEPTION) {
  5074. trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);
  5075. eval('$e = new Exception($this->message, $this->code);throw($e);');
  5076. }
  5077. }
  5078. /**
  5079. * Only here for backwards compatibility.
  5080. *
  5081. * Class "Cache_Error" still uses it, among others.
  5082. *
  5083. * @param string $message Message
  5084. * @param int $code Error code
  5085. * @param int $mode Error mode
  5086. * @param mixed $options See __construct()
  5087. * @param string $userinfo Additional user/debug info
  5088. */
  5089. public function PEAR_Error(
  5090. $message = 'unknown error', $code = null, $mode = null,
  5091. $options = null, $userinfo = null
  5092. ) {
  5093. self::__construct($message, $code, $mode, $options, $userinfo);
  5094. }
  5095. /**
  5096. * Get the error mode from an error object.
  5097. *
  5098. * @return int error mode
  5099. * @access public
  5100. */
  5101. function getMode()
  5102. {
  5103. return $this->mode;
  5104. }
  5105. /**
  5106. * Get the callback function/method from an error object.
  5107. *
  5108. * @return mixed callback function or object/method array
  5109. * @access public
  5110. */
  5111. function getCallback()
  5112. {
  5113. return $this->callback;
  5114. }
  5115. /**
  5116. * Get the error message from an error object.
  5117. *
  5118. * @return string full error message
  5119. * @access public
  5120. */
  5121. function getMessage()
  5122. {
  5123. return ($this->error_message_prefix . $this->message);
  5124. }
  5125. /**
  5126. * Get error code from an error object
  5127. *
  5128. * @return int error code
  5129. * @access public
  5130. */
  5131. function getCode()
  5132. {
  5133. return $this->code;
  5134. }
  5135. /**
  5136. * Get the name of this error/exception.
  5137. *
  5138. * @return string error/exception name (type)
  5139. * @access public
  5140. */
  5141. function getType()
  5142. {
  5143. return get_class($this);
  5144. }
  5145. /**
  5146. * Get additional user-supplied information.
  5147. *
  5148. * @return string user-supplied information
  5149. * @access public
  5150. */
  5151. function getUserInfo()
  5152. {
  5153. return $this->userinfo;
  5154. }
  5155. /**
  5156. * Get additional debug information supplied by the application.
  5157. *
  5158. * @return string debug information
  5159. * @access public
  5160. */
  5161. function getDebugInfo()
  5162. {
  5163. return $this->getUserInfo();
  5164. }
  5165. /**
  5166. * Get the call backtrace from where the error was generated.
  5167. * Supported with PHP 4.3.0 or newer.
  5168. *
  5169. * @param int $frame (optional) what frame to fetch
  5170. * @return array Backtrace, or NULL if not available.
  5171. * @access public
  5172. */
  5173. function getBacktrace($frame = null)
  5174. {
  5175. if (defined('PEAR_IGNORE_BACKTRACE')) {
  5176. return null;
  5177. }
  5178. if ($frame === null) {
  5179. return $this->backtrace;
  5180. }
  5181. return $this->backtrace[$frame];
  5182. }
  5183. function addUserInfo($info)
  5184. {
  5185. if (empty($this->userinfo)) {
  5186. $this->userinfo = $info;
  5187. } else {
  5188. $this->userinfo .= " ** $info";
  5189. }
  5190. }
  5191. function __toString()
  5192. {
  5193. return $this->getMessage();
  5194. }
  5195. /**
  5196. * Make a string representation of this object.
  5197. *
  5198. * @return string a string with an object summary
  5199. * @access public
  5200. */
  5201. function toString()
  5202. {
  5203. $modes = array();
  5204. $levels = array(E_USER_NOTICE => 'notice',
  5205. E_USER_WARNING => 'warning',
  5206. E_USER_ERROR => 'error');
  5207. if ($this->mode & PEAR_ERROR_CALLBACK) {
  5208. if (is_array($this->callback)) {
  5209. $callback = (is_object($this->callback[0]) ?
  5210. strtolower(get_class($this->callback[0])) :
  5211. $this->callback[0]) . '::' .
  5212. $this->callback[1];
  5213. } else {
  5214. $callback = $this->callback;
  5215. }
  5216. return sprintf('[%s: message="%s" code=%d mode=callback '.
  5217. 'callback=%s prefix="%s" info="%s"]',
  5218. strtolower(get_class($this)), $this->message, $this->code,
  5219. $callback, $this->error_message_prefix,
  5220. $this->userinfo);
  5221. }
  5222. if ($this->mode & PEAR_ERROR_PRINT) {
  5223. $modes[] = 'print';
  5224. }
  5225. if ($this->mode & PEAR_ERROR_TRIGGER) {
  5226. $modes[] = 'trigger';
  5227. }
  5228. if ($this->mode & PEAR_ERROR_DIE) {
  5229. $modes[] = 'die';
  5230. }
  5231. if ($this->mode & PEAR_ERROR_RETURN) {
  5232. $modes[] = 'return';
  5233. }
  5234. return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
  5235. 'prefix="%s" info="%s"]',
  5236. strtolower(get_class($this)), $this->message, $this->code,
  5237. implode("|", $modes), $levels[$this->level],
  5238. $this->error_message_prefix,
  5239. $this->userinfo);
  5240. }
  5241. }
  5242. /*
  5243. * Local Variables:
  5244. * mode: php
  5245. * tab-width: 4
  5246. * c-basic-offset: 4
  5247. * End:
  5248. */
  5249. <?php
  5250. /**
  5251. * PEAR_ChannelFile, the channel handling class
  5252. *
  5253. * PHP versions 4 and 5
  5254. *
  5255. * @category pear
  5256. * @package PEAR
  5257. * @author Greg Beaver <cellog@php.net>
  5258. * @copyright 1997-2009 The Authors
  5259. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  5260. * @link http://pear.php.net/package/PEAR
  5261. * @since File available since Release 1.4.0a1
  5262. */
  5263. /**
  5264. * Needed for error handling
  5265. */
  5266. require_once 'phar://go-pear.phar/' . 'PEAR/ErrorStack.php';
  5267. require_once 'phar://go-pear.phar/' . 'PEAR/XMLParser.php';
  5268. require_once 'phar://go-pear.phar/' . 'PEAR/Common.php';
  5269. /**
  5270. * Error code if the channel.xml <channel> tag does not contain a valid version
  5271. */
  5272. define('PEAR_CHANNELFILE_ERROR_NO_VERSION', 1);
  5273. /**
  5274. * Error code if the channel.xml <channel> tag version is not supported (version 1.0 is the only supported version,
  5275. * currently
  5276. */
  5277. define('PEAR_CHANNELFILE_ERROR_INVALID_VERSION', 2);
  5278. /**
  5279. * Error code if parsing is attempted with no xml extension
  5280. */
  5281. define('PEAR_CHANNELFILE_ERROR_NO_XML_EXT', 3);
  5282. /**
  5283. * Error code if creating the xml parser resource fails
  5284. */
  5285. define('PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER', 4);
  5286. /**
  5287. * Error code used for all sax xml parsing errors
  5288. */
  5289. define('PEAR_CHANNELFILE_ERROR_PARSER_ERROR', 5);
  5290. /**#@+
  5291. * Validation errors
  5292. */
  5293. /**
  5294. * Error code when channel name is missing
  5295. */
  5296. define('PEAR_CHANNELFILE_ERROR_NO_NAME', 6);
  5297. /**
  5298. * Error code when channel name is invalid
  5299. */
  5300. define('PEAR_CHANNELFILE_ERROR_INVALID_NAME', 7);
  5301. /**
  5302. * Error code when channel summary is missing
  5303. */
  5304. define('PEAR_CHANNELFILE_ERROR_NO_SUMMARY', 8);
  5305. /**
  5306. * Error code when channel summary is multi-line
  5307. */
  5308. define('PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY', 9);
  5309. /**
  5310. * Error code when channel server is missing for protocol
  5311. */
  5312. define('PEAR_CHANNELFILE_ERROR_NO_HOST', 10);
  5313. /**
  5314. * Error code when channel server is invalid for protocol
  5315. */
  5316. define('PEAR_CHANNELFILE_ERROR_INVALID_HOST', 11);
  5317. /**
  5318. * Error code when a mirror name is invalid
  5319. */
  5320. define('PEAR_CHANNELFILE_ERROR_INVALID_MIRROR', 21);
  5321. /**
  5322. * Error code when a mirror type is invalid
  5323. */
  5324. define('PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE', 22);
  5325. /**
  5326. * Error code when an attempt is made to generate xml, but the parsed content is invalid
  5327. */
  5328. define('PEAR_CHANNELFILE_ERROR_INVALID', 23);
  5329. /**
  5330. * Error code when an empty package name validate regex is passed in
  5331. */
  5332. define('PEAR_CHANNELFILE_ERROR_EMPTY_REGEX', 24);
  5333. /**
  5334. * Error code when a <function> tag has no version
  5335. */
  5336. define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION', 25);
  5337. /**
  5338. * Error code when a <function> tag has no name
  5339. */
  5340. define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME', 26);
  5341. /**
  5342. * Error code when a <validatepackage> tag has no name
  5343. */
  5344. define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME', 27);
  5345. /**
  5346. * Error code when a <validatepackage> tag has no version attribute
  5347. */
  5348. define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION', 28);
  5349. /**
  5350. * Error code when a mirror does not exist but is called for in one of the set*
  5351. * methods.
  5352. */
  5353. define('PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND', 32);
  5354. /**
  5355. * Error code when a server port is not numeric
  5356. */
  5357. define('PEAR_CHANNELFILE_ERROR_INVALID_PORT', 33);
  5358. /**
  5359. * Error code when <static> contains no version attribute
  5360. */
  5361. define('PEAR_CHANNELFILE_ERROR_NO_STATICVERSION', 34);
  5362. /**
  5363. * Error code when <baseurl> contains no type attribute in a <rest> protocol definition
  5364. */
  5365. define('PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE', 35);
  5366. /**
  5367. * Error code when a mirror is defined and the channel.xml represents the __uri pseudo-channel
  5368. */
  5369. define('PEAR_CHANNELFILE_URI_CANT_MIRROR', 36);
  5370. /**
  5371. * Error code when ssl attribute is present and is not "yes"
  5372. */
  5373. define('PEAR_CHANNELFILE_ERROR_INVALID_SSL', 37);
  5374. /**#@-*/
  5375. /**
  5376. * Mirror types allowed. Currently only internet servers are recognized.
  5377. */
  5378. $GLOBALS['_PEAR_CHANNELS_MIRROR_TYPES'] = array('server');
  5379. /**
  5380. * The Channel handling class
  5381. *
  5382. * @category pear
  5383. * @package PEAR
  5384. * @author Greg Beaver <cellog@php.net>
  5385. * @copyright 1997-2009 The Authors
  5386. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  5387. * @version Release: 1.10.16
  5388. * @link http://pear.php.net/package/PEAR
  5389. * @since Class available since Release 1.4.0a1
  5390. */
  5391. class PEAR_ChannelFile
  5392. {
  5393. /**
  5394. * @access private
  5395. * @var PEAR_ErrorStack
  5396. * @access private
  5397. */
  5398. var $_stack;
  5399. /**
  5400. * Supported channel.xml versions, for parsing
  5401. * @var array
  5402. * @access private
  5403. */
  5404. var $_supportedVersions = array('1.0');
  5405. /**
  5406. * Parsed channel information
  5407. * @var array
  5408. * @access private
  5409. */
  5410. var $_channelInfo;
  5411. /**
  5412. * index into the subchannels array, used for parsing xml
  5413. * @var int
  5414. * @access private
  5415. */
  5416. var $_subchannelIndex;
  5417. /**
  5418. * index into the mirrors array, used for parsing xml
  5419. * @var int
  5420. * @access private
  5421. */
  5422. var $_mirrorIndex;
  5423. /**
  5424. * Flag used to determine the validity of parsed content
  5425. * @var boolean
  5426. * @access private
  5427. */
  5428. var $_isValid = false;
  5429. function __construct()
  5430. {
  5431. $this->_stack = new PEAR_ErrorStack('PEAR_ChannelFile');
  5432. $this->_stack->setErrorMessageTemplate($this->_getErrorMessage());
  5433. $this->_isValid = false;
  5434. }
  5435. /**
  5436. * @return array
  5437. * @access protected
  5438. */
  5439. function _getErrorMessage()
  5440. {
  5441. return
  5442. array(
  5443. PEAR_CHANNELFILE_ERROR_INVALID_VERSION =>
  5444. 'While parsing channel.xml, an invalid version number "%version% was passed in, expecting one of %versions%',
  5445. PEAR_CHANNELFILE_ERROR_NO_VERSION =>
  5446. 'No version number found in <channel> tag',
  5447. PEAR_CHANNELFILE_ERROR_NO_XML_EXT =>
  5448. '%error%',
  5449. PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER =>
  5450. 'Unable to create XML parser',
  5451. PEAR_CHANNELFILE_ERROR_PARSER_ERROR =>
  5452. '%error%',
  5453. PEAR_CHANNELFILE_ERROR_NO_NAME =>
  5454. 'Missing channel name',
  5455. PEAR_CHANNELFILE_ERROR_INVALID_NAME =>
  5456. 'Invalid channel %tag% "%name%"',
  5457. PEAR_CHANNELFILE_ERROR_NO_SUMMARY =>
  5458. 'Missing channel summary',
  5459. PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY =>
  5460. 'Channel summary should be on one line, but is multi-line',
  5461. PEAR_CHANNELFILE_ERROR_NO_HOST =>
  5462. 'Missing channel server for %type% server',
  5463. PEAR_CHANNELFILE_ERROR_INVALID_HOST =>
  5464. 'Server name "%server%" is invalid for %type% server',
  5465. PEAR_CHANNELFILE_ERROR_INVALID_MIRROR =>
  5466. 'Invalid mirror name "%name%", mirror type %type%',
  5467. PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE =>
  5468. 'Invalid mirror type "%type%"',
  5469. PEAR_CHANNELFILE_ERROR_INVALID =>
  5470. 'Cannot generate xml, contents are invalid',
  5471. PEAR_CHANNELFILE_ERROR_EMPTY_REGEX =>
  5472. 'packagenameregex cannot be empty',
  5473. PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION =>
  5474. '%parent% %protocol% function has no version',
  5475. PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME =>
  5476. '%parent% %protocol% function has no name',
  5477. PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE =>
  5478. '%parent% rest baseurl has no type',
  5479. PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME =>
  5480. 'Validation package has no name in <validatepackage> tag',
  5481. PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION =>
  5482. 'Validation package "%package%" has no version',
  5483. PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND =>
  5484. 'Mirror "%mirror%" does not exist',
  5485. PEAR_CHANNELFILE_ERROR_INVALID_PORT =>
  5486. 'Port "%port%" must be numeric',
  5487. PEAR_CHANNELFILE_ERROR_NO_STATICVERSION =>
  5488. '<static> tag must contain version attribute',
  5489. PEAR_CHANNELFILE_URI_CANT_MIRROR =>
  5490. 'The __uri pseudo-channel cannot have mirrors',
  5491. PEAR_CHANNELFILE_ERROR_INVALID_SSL =>
  5492. '%server% has invalid ssl attribute "%ssl%" can only be yes or not present',
  5493. );
  5494. }
  5495. /**
  5496. * @param string contents of package.xml file
  5497. * @return bool success of parsing
  5498. */
  5499. function fromXmlString($data)
  5500. {
  5501. if (preg_match('/<channel\s+version="([0-9]+\.[0-9]+)"/', $data, $channelversion)) {
  5502. if (!in_array($channelversion[1], $this->_supportedVersions)) {
  5503. $this->_stack->push(PEAR_CHANNELFILE_ERROR_INVALID_VERSION, 'error',
  5504. array('version' => $channelversion[1]));
  5505. return false;
  5506. }
  5507. $parser = new PEAR_XMLParser;
  5508. $result = $parser->parse($data);
  5509. if ($result !== true) {
  5510. if ($result->getCode() == 1) {
  5511. $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_XML_EXT, 'error',
  5512. array('error' => $result->getMessage()));
  5513. } else {
  5514. $this->_stack->push(PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER, 'error');
  5515. }
  5516. return false;
  5517. }
  5518. $this->_channelInfo = $parser->getData();
  5519. return true;
  5520. } else {
  5521. $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_VERSION, 'error', array('xml' => $data));
  5522. return false;
  5523. }
  5524. }
  5525. /**
  5526. * @return array
  5527. */
  5528. function toArray()
  5529. {
  5530. if (!$this->_isValid && !$this->validate()) {
  5531. return false;
  5532. }
  5533. return $this->_channelInfo;
  5534. }
  5535. /**
  5536. * @param array
  5537. *
  5538. * @return PEAR_ChannelFile|false false if invalid
  5539. */
  5540. public static function &fromArray(
  5541. $data, $compatibility = false, $stackClass = 'PEAR_ErrorStack'
  5542. ) {
  5543. $a = new PEAR_ChannelFile($compatibility, $stackClass);
  5544. $a->_fromArray($data);
  5545. if (!$a->validate()) {
  5546. $a = false;
  5547. return $a;
  5548. }
  5549. return $a;
  5550. }
  5551. /**
  5552. * Unlike {@link fromArray()} this does not do any validation
  5553. *
  5554. * @param array
  5555. *
  5556. * @return PEAR_ChannelFile
  5557. */
  5558. public static function &fromArrayWithErrors(
  5559. $data, $compatibility = false, $stackClass = 'PEAR_ErrorStack'
  5560. ) {
  5561. $a = new PEAR_ChannelFile($compatibility, $stackClass);
  5562. $a->_fromArray($data);
  5563. return $a;
  5564. }
  5565. /**
  5566. * @param array
  5567. * @access private
  5568. */
  5569. function _fromArray($data)
  5570. {
  5571. $this->_channelInfo = $data;
  5572. }
  5573. /**
  5574. * Wrapper to {@link PEAR_ErrorStack::getErrors()}
  5575. * @param boolean determines whether to purge the error stack after retrieving
  5576. * @return array
  5577. */
  5578. function getErrors($purge = false)
  5579. {
  5580. return $this->_stack->getErrors($purge);
  5581. }
  5582. /**
  5583. * Unindent given string (?)
  5584. *
  5585. * @param string $str The string that has to be unindented.
  5586. * @return string
  5587. * @access private
  5588. */
  5589. function _unIndent($str)
  5590. {
  5591. // remove leading newlines
  5592. $str = preg_replace('/^[\r\n]+/', '', $str);
  5593. // find whitespace at the beginning of the first line
  5594. $indent_len = strspn($str, " \t");
  5595. $indent = substr($str, 0, $indent_len);
  5596. $data = '';
  5597. // remove the same amount of whitespace from following lines
  5598. foreach (explode("\n", $str) as $line) {
  5599. if (substr($line, 0, $indent_len) == $indent) {
  5600. $data .= substr($line, $indent_len) . "\n";
  5601. }
  5602. }
  5603. return $data;
  5604. }
  5605. /**
  5606. * Parse a channel.xml file. Expects the name of
  5607. * a channel xml file as input.
  5608. *
  5609. * @param string $descfile name of channel xml file
  5610. * @return bool success of parsing
  5611. */
  5612. function fromXmlFile($descfile)
  5613. {
  5614. if (!file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) ||
  5615. (!$fp = fopen($descfile, 'r'))) {
  5616. require_once 'phar://go-pear.phar/' . 'PEAR.php';
  5617. return PEAR::raiseError("Unable to open $descfile");
  5618. }
  5619. // read the whole thing so we only get one cdata callback
  5620. // for each block of cdata
  5621. fclose($fp);
  5622. $data = file_get_contents($descfile);
  5623. return $this->fromXmlString($data);
  5624. }
  5625. /**
  5626. * Parse channel information from different sources
  5627. *
  5628. * This method is able to extract information about a channel
  5629. * from an .xml file or a string
  5630. *
  5631. * @access public
  5632. * @param string Filename of the source or the source itself
  5633. * @return bool
  5634. */
  5635. function fromAny($info)
  5636. {
  5637. if (is_string($info) && file_exists($info) && strlen($info) < 255) {
  5638. $tmp = substr($info, -4);
  5639. if ($tmp == '.xml') {
  5640. $info = $this->fromXmlFile($info);
  5641. } else {
  5642. $fp = fopen($info, "r");
  5643. $test = fread($fp, 5);
  5644. fclose($fp);
  5645. if ($test == "<?xml") {
  5646. $info = $this->fromXmlFile($info);
  5647. }
  5648. }
  5649. if (PEAR::isError($info)) {
  5650. require_once 'phar://go-pear.phar/' . 'PEAR.php';
  5651. return PEAR::raiseError($info);
  5652. }
  5653. }
  5654. if (is_string($info)) {
  5655. $info = $this->fromXmlString($info);
  5656. }
  5657. return $info;
  5658. }
  5659. /**
  5660. * Return an XML document based on previous parsing and modifications
  5661. *
  5662. * @return string XML data
  5663. *
  5664. * @access public
  5665. */
  5666. function toXml()
  5667. {
  5668. if (!$this->_isValid && !$this->validate()) {
  5669. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID);
  5670. return false;
  5671. }
  5672. if (!isset($this->_channelInfo['attribs']['version'])) {
  5673. $this->_channelInfo['attribs']['version'] = '1.0';
  5674. }
  5675. $channelInfo = $this->_channelInfo;
  5676. $ret = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
  5677. $ret .= "<channel version=\"" .
  5678. $channelInfo['attribs']['version'] . "\" xmlns=\"http://pear.php.net/channel-1.0\"
  5679. xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
  5680. xsi:schemaLocation=\"http://pear.php.net/dtd/channel-"
  5681. . $channelInfo['attribs']['version'] . " http://pear.php.net/dtd/channel-" .
  5682. $channelInfo['attribs']['version'] . ".xsd\">
  5683. <name>$channelInfo[name]</name>
  5684. <summary>" . htmlspecialchars($channelInfo['summary'])."</summary>
  5685. ";
  5686. if (isset($channelInfo['suggestedalias'])) {
  5687. $ret .= ' <suggestedalias>' . $channelInfo['suggestedalias'] . "</suggestedalias>\n";
  5688. }
  5689. if (isset($channelInfo['validatepackage'])) {
  5690. $ret .= ' <validatepackage version="' .
  5691. $channelInfo['validatepackage']['attribs']['version']. '">' .
  5692. htmlspecialchars($channelInfo['validatepackage']['_content']) .
  5693. "</validatepackage>\n";
  5694. }
  5695. $ret .= " <servers>\n";
  5696. $ret .= ' <primary';
  5697. if (isset($channelInfo['servers']['primary']['attribs']['ssl'])) {
  5698. $ret .= ' ssl="' . $channelInfo['servers']['primary']['attribs']['ssl'] . '"';
  5699. }
  5700. if (isset($channelInfo['servers']['primary']['attribs']['port'])) {
  5701. $ret .= ' port="' . $channelInfo['servers']['primary']['attribs']['port'] . '"';
  5702. }
  5703. $ret .= ">\n";
  5704. if (isset($channelInfo['servers']['primary']['rest'])) {
  5705. $ret .= $this->_makeRestXml($channelInfo['servers']['primary']['rest'], ' ');
  5706. }
  5707. $ret .= " </primary>\n";
  5708. if (isset($channelInfo['servers']['mirror'])) {
  5709. $ret .= $this->_makeMirrorsXml($channelInfo);
  5710. }
  5711. $ret .= " </servers>\n";
  5712. $ret .= "</channel>";
  5713. return str_replace("\r", "\n", str_replace("\r\n", "\n", $ret));
  5714. }
  5715. /**
  5716. * Generate the <rest> tag
  5717. * @access private
  5718. */
  5719. function _makeRestXml($info, $indent)
  5720. {
  5721. $ret = $indent . "<rest>\n";
  5722. if (isset($info['baseurl']) && !isset($info['baseurl'][0])) {
  5723. $info['baseurl'] = array($info['baseurl']);
  5724. }
  5725. if (isset($info['baseurl'])) {
  5726. foreach ($info['baseurl'] as $url) {
  5727. $ret .= "$indent <baseurl type=\"" . $url['attribs']['type'] . "\"";
  5728. $ret .= ">" . $url['_content'] . "</baseurl>\n";
  5729. }
  5730. }
  5731. $ret .= $indent . "</rest>\n";
  5732. return $ret;
  5733. }
  5734. /**
  5735. * Generate the <mirrors> tag
  5736. * @access private
  5737. */
  5738. function _makeMirrorsXml($channelInfo)
  5739. {
  5740. $ret = "";
  5741. if (!isset($channelInfo['servers']['mirror'][0])) {
  5742. $channelInfo['servers']['mirror'] = array($channelInfo['servers']['mirror']);
  5743. }
  5744. foreach ($channelInfo['servers']['mirror'] as $mirror) {
  5745. $ret .= ' <mirror host="' . $mirror['attribs']['host'] . '"';
  5746. if (isset($mirror['attribs']['port'])) {
  5747. $ret .= ' port="' . $mirror['attribs']['port'] . '"';
  5748. }
  5749. if (isset($mirror['attribs']['ssl'])) {
  5750. $ret .= ' ssl="' . $mirror['attribs']['ssl'] . '"';
  5751. }
  5752. $ret .= ">\n";
  5753. if (isset($mirror['rest'])) {
  5754. if (isset($mirror['rest'])) {
  5755. $ret .= $this->_makeRestXml($mirror['rest'], ' ');
  5756. }
  5757. $ret .= " </mirror>\n";
  5758. } else {
  5759. $ret .= "/>\n";
  5760. }
  5761. }
  5762. return $ret;
  5763. }
  5764. /**
  5765. * Generate the <functions> tag
  5766. * @access private
  5767. */
  5768. function _makeFunctionsXml($functions, $indent, $rest = false)
  5769. {
  5770. $ret = '';
  5771. if (!isset($functions[0])) {
  5772. $functions = array($functions);
  5773. }
  5774. foreach ($functions as $function) {
  5775. $ret .= "$indent<function version=\"" . $function['attribs']['version'] . "\"";
  5776. if ($rest) {
  5777. $ret .= ' uri="' . $function['attribs']['uri'] . '"';
  5778. }
  5779. $ret .= ">" . $function['_content'] . "</function>\n";
  5780. }
  5781. return $ret;
  5782. }
  5783. /**
  5784. * Validation error. Also marks the object contents as invalid
  5785. * @param error code
  5786. * @param array error information
  5787. * @access private
  5788. */
  5789. function _validateError($code, $params = array())
  5790. {
  5791. $this->_stack->push($code, 'error', $params);
  5792. $this->_isValid = false;
  5793. }
  5794. /**
  5795. * Validation warning. Does not mark the object contents invalid.
  5796. * @param error code
  5797. * @param array error information
  5798. * @access private
  5799. */
  5800. function _validateWarning($code, $params = array())
  5801. {
  5802. $this->_stack->push($code, 'warning', $params);
  5803. }
  5804. /**
  5805. * Validate parsed file.
  5806. *
  5807. * @access public
  5808. * @return boolean
  5809. */
  5810. function validate()
  5811. {
  5812. $this->_isValid = true;
  5813. $info = $this->_channelInfo;
  5814. if (empty($info['name'])) {
  5815. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_NAME);
  5816. } elseif (!$this->validChannelServer($info['name'])) {
  5817. if ($info['name'] != '__uri') {
  5818. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, array('tag' => 'name',
  5819. 'name' => $info['name']));
  5820. }
  5821. }
  5822. if (empty($info['summary'])) {
  5823. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
  5824. } elseif (strpos(trim($info['summary']), "\n") !== false) {
  5825. $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
  5826. array('summary' => $info['summary']));
  5827. }
  5828. if (isset($info['suggestedalias'])) {
  5829. if (!$this->validChannelServer($info['suggestedalias'])) {
  5830. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
  5831. array('tag' => 'suggestedalias', 'name' =>$info['suggestedalias']));
  5832. }
  5833. }
  5834. if (isset($info['localalias'])) {
  5835. if (!$this->validChannelServer($info['localalias'])) {
  5836. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
  5837. array('tag' => 'localalias', 'name' =>$info['localalias']));
  5838. }
  5839. }
  5840. if (isset($info['validatepackage'])) {
  5841. if (!isset($info['validatepackage']['_content'])) {
  5842. $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME);
  5843. }
  5844. if (!isset($info['validatepackage']['attribs']['version'])) {
  5845. $content = isset($info['validatepackage']['_content']) ?
  5846. $info['validatepackage']['_content'] :
  5847. null;
  5848. $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION,
  5849. array('package' => $content));
  5850. }
  5851. }
  5852. if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['port']) &&
  5853. !is_numeric($info['servers']['primary']['attribs']['port'])) {
  5854. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_PORT,
  5855. array('port' => $info['servers']['primary']['attribs']['port']));
  5856. }
  5857. if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['ssl']) &&
  5858. $info['servers']['primary']['attribs']['ssl'] != 'yes') {
  5859. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
  5860. array('ssl' => $info['servers']['primary']['attribs']['ssl'],
  5861. 'server' => $info['name']));
  5862. }
  5863. if (isset($info['servers']['primary']['rest']) &&
  5864. isset($info['servers']['primary']['rest']['baseurl'])) {
  5865. $this->_validateFunctions('rest', $info['servers']['primary']['rest']['baseurl']);
  5866. }
  5867. if (isset($info['servers']['mirror'])) {
  5868. if ($this->_channelInfo['name'] == '__uri') {
  5869. $this->_validateError(PEAR_CHANNELFILE_URI_CANT_MIRROR);
  5870. }
  5871. if (!isset($info['servers']['mirror'][0])) {
  5872. $info['servers']['mirror'] = array($info['servers']['mirror']);
  5873. }
  5874. foreach ($info['servers']['mirror'] as $mirror) {
  5875. if (!isset($mirror['attribs']['host'])) {
  5876. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_HOST,
  5877. array('type' => 'mirror'));
  5878. } elseif (!$this->validChannelServer($mirror['attribs']['host'])) {
  5879. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_HOST,
  5880. array('server' => $mirror['attribs']['host'], 'type' => 'mirror'));
  5881. }
  5882. if (isset($mirror['attribs']['ssl']) && $mirror['attribs']['ssl'] != 'yes') {
  5883. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
  5884. array('ssl' => $info['ssl'], 'server' => $mirror['attribs']['host']));
  5885. }
  5886. if (isset($mirror['rest'])) {
  5887. $this->_validateFunctions('rest', $mirror['rest']['baseurl'],
  5888. $mirror['attribs']['host']);
  5889. }
  5890. }
  5891. }
  5892. return $this->_isValid;
  5893. }
  5894. /**
  5895. * @param string rest - protocol name this function applies to
  5896. * @param array the functions
  5897. * @param string the name of the parent element (mirror name, for instance)
  5898. */
  5899. function _validateFunctions($protocol, $functions, $parent = '')
  5900. {
  5901. if (!isset($functions[0])) {
  5902. $functions = array($functions);
  5903. }
  5904. foreach ($functions as $function) {
  5905. if (!isset($function['_content']) || empty($function['_content'])) {
  5906. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME,
  5907. array('parent' => $parent, 'protocol' => $protocol));
  5908. }
  5909. if ($protocol == 'rest') {
  5910. if (!isset($function['attribs']['type']) ||
  5911. empty($function['attribs']['type'])) {
  5912. $this->_validateError(PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE,
  5913. array('parent' => $parent, 'protocol' => $protocol));
  5914. }
  5915. } else {
  5916. if (!isset($function['attribs']['version']) ||
  5917. empty($function['attribs']['version'])) {
  5918. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION,
  5919. array('parent' => $parent, 'protocol' => $protocol));
  5920. }
  5921. }
  5922. }
  5923. }
  5924. /**
  5925. * Test whether a string contains a valid channel server.
  5926. * @param string $ver the package version to test
  5927. * @return bool
  5928. */
  5929. function validChannelServer($server)
  5930. {
  5931. if ($server == '__uri') {
  5932. return true;
  5933. }
  5934. return (bool) preg_match(PEAR_CHANNELS_SERVER_PREG, $server);
  5935. }
  5936. /**
  5937. * @return string|false
  5938. */
  5939. function getName()
  5940. {
  5941. if (isset($this->_channelInfo['name'])) {
  5942. return $this->_channelInfo['name'];
  5943. }
  5944. return false;
  5945. }
  5946. /**
  5947. * @return string|false
  5948. */
  5949. function getServer()
  5950. {
  5951. if (isset($this->_channelInfo['name'])) {
  5952. return $this->_channelInfo['name'];
  5953. }
  5954. return false;
  5955. }
  5956. /**
  5957. * @return int|80 port number to connect to
  5958. */
  5959. function getPort($mirror = false)
  5960. {
  5961. if ($mirror) {
  5962. if ($mir = $this->getMirror($mirror)) {
  5963. if (isset($mir['attribs']['port'])) {
  5964. return $mir['attribs']['port'];
  5965. }
  5966. if ($this->getSSL($mirror)) {
  5967. return 443;
  5968. }
  5969. return 80;
  5970. }
  5971. return false;
  5972. }
  5973. if (isset($this->_channelInfo['servers']['primary']['attribs']['port'])) {
  5974. return $this->_channelInfo['servers']['primary']['attribs']['port'];
  5975. }
  5976. if ($this->getSSL()) {
  5977. return 443;
  5978. }
  5979. return 80;
  5980. }
  5981. /**
  5982. * @return bool Determines whether secure sockets layer (SSL) is used to connect to this channel
  5983. */
  5984. function getSSL($mirror = false)
  5985. {
  5986. if ($mirror) {
  5987. if ($mir = $this->getMirror($mirror)) {
  5988. if (isset($mir['attribs']['ssl'])) {
  5989. return true;
  5990. }
  5991. return false;
  5992. }
  5993. return false;
  5994. }
  5995. if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
  5996. return true;
  5997. }
  5998. return false;
  5999. }
  6000. /**
  6001. * @return string|false
  6002. */
  6003. function getSummary()
  6004. {
  6005. if (isset($this->_channelInfo['summary'])) {
  6006. return $this->_channelInfo['summary'];
  6007. }
  6008. return false;
  6009. }
  6010. /**
  6011. * @param string protocol type
  6012. * @param string Mirror name
  6013. * @return array|false
  6014. */
  6015. function getFunctions($protocol, $mirror = false)
  6016. {
  6017. if ($this->getName() == '__uri') {
  6018. return false;
  6019. }
  6020. $function = $protocol == 'rest' ? 'baseurl' : 'function';
  6021. if ($mirror) {
  6022. if ($mir = $this->getMirror($mirror)) {
  6023. if (isset($mir[$protocol][$function])) {
  6024. return $mir[$protocol][$function];
  6025. }
  6026. }
  6027. return false;
  6028. }
  6029. if (isset($this->_channelInfo['servers']['primary'][$protocol][$function])) {
  6030. return $this->_channelInfo['servers']['primary'][$protocol][$function];
  6031. }
  6032. return false;
  6033. }
  6034. /**
  6035. * @param string Protocol type
  6036. * @param string Function name (null to return the
  6037. * first protocol of the type requested)
  6038. * @param string Mirror name, if any
  6039. * @return array
  6040. */
  6041. function getFunction($type, $name = null, $mirror = false)
  6042. {
  6043. $protocols = $this->getFunctions($type, $mirror);
  6044. if (!$protocols) {
  6045. return false;
  6046. }
  6047. foreach ($protocols as $protocol) {
  6048. if ($name === null) {
  6049. return $protocol;
  6050. }
  6051. if ($protocol['_content'] != $name) {
  6052. continue;
  6053. }
  6054. return $protocol;
  6055. }
  6056. return false;
  6057. }
  6058. /**
  6059. * @param string protocol type
  6060. * @param string protocol name
  6061. * @param string version
  6062. * @param string mirror name
  6063. * @return boolean
  6064. */
  6065. function supports($type, $name = null, $mirror = false, $version = '1.0')
  6066. {
  6067. $protocols = $this->getFunctions($type, $mirror);
  6068. if (!$protocols) {
  6069. return false;
  6070. }
  6071. foreach ($protocols as $protocol) {
  6072. if ($protocol['attribs']['version'] != $version) {
  6073. continue;
  6074. }
  6075. if ($name === null) {
  6076. return true;
  6077. }
  6078. if ($protocol['_content'] != $name) {
  6079. continue;
  6080. }
  6081. return true;
  6082. }
  6083. return false;
  6084. }
  6085. /**
  6086. * Determines whether a channel supports Representational State Transfer (REST) protocols
  6087. * for retrieving channel information
  6088. * @param string
  6089. * @return bool
  6090. */
  6091. function supportsREST($mirror = false)
  6092. {
  6093. if ($mirror == $this->_channelInfo['name']) {
  6094. $mirror = false;
  6095. }
  6096. if ($mirror) {
  6097. if ($mir = $this->getMirror($mirror)) {
  6098. return isset($mir['rest']);
  6099. }
  6100. return false;
  6101. }
  6102. return isset($this->_channelInfo['servers']['primary']['rest']);
  6103. }
  6104. /**
  6105. * Get the URL to access a base resource.
  6106. *
  6107. * Hyperlinks in the returned xml will be used to retrieve the proper information
  6108. * needed. This allows extreme extensibility and flexibility in implementation
  6109. * @param string Resource Type to retrieve
  6110. */
  6111. function getBaseURL($resourceType, $mirror = false)
  6112. {
  6113. if ($mirror == $this->_channelInfo['name']) {
  6114. $mirror = false;
  6115. }
  6116. if ($mirror) {
  6117. $mir = $this->getMirror($mirror);
  6118. if (!$mir) {
  6119. return false;
  6120. }
  6121. $rest = $mir['rest'];
  6122. } else {
  6123. $rest = $this->_channelInfo['servers']['primary']['rest'];
  6124. }
  6125. if (!isset($rest['baseurl'][0])) {
  6126. $rest['baseurl'] = array($rest['baseurl']);
  6127. }
  6128. foreach ($rest['baseurl'] as $baseurl) {
  6129. if (strtolower($baseurl['attribs']['type']) == strtolower($resourceType)) {
  6130. return $baseurl['_content'];
  6131. }
  6132. }
  6133. return false;
  6134. }
  6135. /**
  6136. * Since REST does not implement RPC, provide this as a logical wrapper around
  6137. * resetFunctions for REST
  6138. * @param string|false mirror name, if any
  6139. */
  6140. function resetREST($mirror = false)
  6141. {
  6142. return $this->resetFunctions('rest', $mirror);
  6143. }
  6144. /**
  6145. * Empty all protocol definitions
  6146. * @param string protocol type
  6147. * @param string|false mirror name, if any
  6148. */
  6149. function resetFunctions($type, $mirror = false)
  6150. {
  6151. if ($mirror) {
  6152. if (isset($this->_channelInfo['servers']['mirror'])) {
  6153. $mirrors = $this->_channelInfo['servers']['mirror'];
  6154. if (!isset($mirrors[0])) {
  6155. $mirrors = array($mirrors);
  6156. }
  6157. foreach ($mirrors as $i => $mir) {
  6158. if ($mir['attribs']['host'] == $mirror) {
  6159. if (isset($this->_channelInfo['servers']['mirror'][$i][$type])) {
  6160. unset($this->_channelInfo['servers']['mirror'][$i][$type]);
  6161. }
  6162. return true;
  6163. }
  6164. }
  6165. return false;
  6166. }
  6167. return false;
  6168. }
  6169. if (isset($this->_channelInfo['servers']['primary'][$type])) {
  6170. unset($this->_channelInfo['servers']['primary'][$type]);
  6171. }
  6172. return true;
  6173. }
  6174. /**
  6175. * Set a channel's protocols to the protocols supported by pearweb
  6176. */
  6177. function setDefaultPEARProtocols($version = '1.0', $mirror = false)
  6178. {
  6179. switch ($version) {
  6180. case '1.0' :
  6181. $this->resetREST($mirror);
  6182. if (!isset($this->_channelInfo['servers'])) {
  6183. $this->_channelInfo['servers'] = array('primary' =>
  6184. array('rest' => array()));
  6185. } elseif (!isset($this->_channelInfo['servers']['primary'])) {
  6186. $this->_channelInfo['servers']['primary'] = array('rest' => array());
  6187. }
  6188. return true;
  6189. break;
  6190. default :
  6191. return false;
  6192. break;
  6193. }
  6194. }
  6195. /**
  6196. * @return array
  6197. */
  6198. function getMirrors()
  6199. {
  6200. if (isset($this->_channelInfo['servers']['mirror'])) {
  6201. $mirrors = $this->_channelInfo['servers']['mirror'];
  6202. if (!isset($mirrors[0])) {
  6203. $mirrors = array($mirrors);
  6204. }
  6205. return $mirrors;
  6206. }
  6207. return array();
  6208. }
  6209. /**
  6210. * Get the unserialized XML representing a mirror
  6211. * @return array|false
  6212. */
  6213. function getMirror($server)
  6214. {
  6215. foreach ($this->getMirrors() as $mirror) {
  6216. if ($mirror['attribs']['host'] == $server) {
  6217. return $mirror;
  6218. }
  6219. }
  6220. return false;
  6221. }
  6222. /**
  6223. * @param string
  6224. * @return string|false
  6225. * @error PEAR_CHANNELFILE_ERROR_NO_NAME
  6226. * @error PEAR_CHANNELFILE_ERROR_INVALID_NAME
  6227. */
  6228. function setName($name)
  6229. {
  6230. return $this->setServer($name);
  6231. }
  6232. /**
  6233. * Set the socket number (port) that is used to connect to this channel
  6234. * @param integer
  6235. * @param string|false name of the mirror server, or false for the primary
  6236. */
  6237. function setPort($port, $mirror = false)
  6238. {
  6239. if ($mirror) {
  6240. if (!isset($this->_channelInfo['servers']['mirror'])) {
  6241. $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  6242. array('mirror' => $mirror));
  6243. return false;
  6244. }
  6245. if (isset($this->_channelInfo['servers']['mirror'][0])) {
  6246. foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  6247. if ($mirror == $mir['attribs']['host']) {
  6248. $this->_channelInfo['servers']['mirror'][$i]['attribs']['port'] = $port;
  6249. return true;
  6250. }
  6251. }
  6252. return false;
  6253. } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
  6254. $this->_channelInfo['servers']['mirror']['attribs']['port'] = $port;
  6255. $this->_isValid = false;
  6256. return true;
  6257. }
  6258. }
  6259. $this->_channelInfo['servers']['primary']['attribs']['port'] = $port;
  6260. $this->_isValid = false;
  6261. return true;
  6262. }
  6263. /**
  6264. * Set the socket number (port) that is used to connect to this channel
  6265. * @param bool Determines whether to turn on SSL support or turn it off
  6266. * @param string|false name of the mirror server, or false for the primary
  6267. */
  6268. function setSSL($ssl = true, $mirror = false)
  6269. {
  6270. if ($mirror) {
  6271. if (!isset($this->_channelInfo['servers']['mirror'])) {
  6272. $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  6273. array('mirror' => $mirror));
  6274. return false;
  6275. }
  6276. if (isset($this->_channelInfo['servers']['mirror'][0])) {
  6277. foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  6278. if ($mirror == $mir['attribs']['host']) {
  6279. if (!$ssl) {
  6280. if (isset($this->_channelInfo['servers']['mirror'][$i]
  6281. ['attribs']['ssl'])) {
  6282. unset($this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl']);
  6283. }
  6284. } else {
  6285. $this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl'] = 'yes';
  6286. }
  6287. return true;
  6288. }
  6289. }
  6290. return false;
  6291. } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
  6292. if (!$ssl) {
  6293. if (isset($this->_channelInfo['servers']['mirror']['attribs']['ssl'])) {
  6294. unset($this->_channelInfo['servers']['mirror']['attribs']['ssl']);
  6295. }
  6296. } else {
  6297. $this->_channelInfo['servers']['mirror']['attribs']['ssl'] = 'yes';
  6298. }
  6299. $this->_isValid = false;
  6300. return true;
  6301. }
  6302. }
  6303. if ($ssl) {
  6304. $this->_channelInfo['servers']['primary']['attribs']['ssl'] = 'yes';
  6305. } else {
  6306. if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
  6307. unset($this->_channelInfo['servers']['primary']['attribs']['ssl']);
  6308. }
  6309. }
  6310. $this->_isValid = false;
  6311. return true;
  6312. }
  6313. /**
  6314. * @param string
  6315. * @return string|false
  6316. * @error PEAR_CHANNELFILE_ERROR_NO_SERVER
  6317. * @error PEAR_CHANNELFILE_ERROR_INVALID_SERVER
  6318. */
  6319. function setServer($server, $mirror = false)
  6320. {
  6321. if (empty($server)) {
  6322. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SERVER);
  6323. return false;
  6324. } elseif (!$this->validChannelServer($server)) {
  6325. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
  6326. array('tag' => 'name', 'name' => $server));
  6327. return false;
  6328. }
  6329. if ($mirror) {
  6330. $found = false;
  6331. foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  6332. if ($mirror == $mir['attribs']['host']) {
  6333. $found = true;
  6334. break;
  6335. }
  6336. }
  6337. if (!$found) {
  6338. $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  6339. array('mirror' => $mirror));
  6340. return false;
  6341. }
  6342. $this->_channelInfo['mirror'][$i]['attribs']['host'] = $server;
  6343. return true;
  6344. }
  6345. $this->_channelInfo['name'] = $server;
  6346. return true;
  6347. }
  6348. /**
  6349. * @param string
  6350. * @return boolean success
  6351. * @error PEAR_CHANNELFILE_ERROR_NO_SUMMARY
  6352. * @warning PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY
  6353. */
  6354. function setSummary($summary)
  6355. {
  6356. if (empty($summary)) {
  6357. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
  6358. return false;
  6359. } elseif (strpos(trim($summary), "\n") !== false) {
  6360. $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
  6361. array('summary' => $summary));
  6362. }
  6363. $this->_channelInfo['summary'] = $summary;
  6364. return true;
  6365. }
  6366. /**
  6367. * @param string
  6368. * @param boolean determines whether the alias is in channel.xml or local
  6369. * @return boolean success
  6370. */
  6371. function setAlias($alias, $local = false)
  6372. {
  6373. if (!$this->validChannelServer($alias)) {
  6374. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
  6375. array('tag' => 'suggestedalias', 'name' => $alias));
  6376. return false;
  6377. }
  6378. if ($local) {
  6379. $this->_channelInfo['localalias'] = $alias;
  6380. } else {
  6381. $this->_channelInfo['suggestedalias'] = $alias;
  6382. }
  6383. return true;
  6384. }
  6385. /**
  6386. * @return string
  6387. */
  6388. function getAlias()
  6389. {
  6390. if (isset($this->_channelInfo['localalias'])) {
  6391. return $this->_channelInfo['localalias'];
  6392. }
  6393. if (isset($this->_channelInfo['suggestedalias'])) {
  6394. return $this->_channelInfo['suggestedalias'];
  6395. }
  6396. if (isset($this->_channelInfo['name'])) {
  6397. return $this->_channelInfo['name'];
  6398. }
  6399. return '';
  6400. }
  6401. /**
  6402. * Set the package validation object if it differs from PEAR's default
  6403. * The class must be includeable via changing _ in the classname to path separator,
  6404. * but no checking of this is made.
  6405. * @param string|false pass in false to reset to the default packagename regex
  6406. * @return boolean success
  6407. */
  6408. function setValidationPackage($validateclass, $version)
  6409. {
  6410. if (empty($validateclass)) {
  6411. unset($this->_channelInfo['validatepackage']);
  6412. }
  6413. $this->_channelInfo['validatepackage'] = array('_content' => $validateclass);
  6414. $this->_channelInfo['validatepackage']['attribs'] = array('version' => $version);
  6415. }
  6416. /**
  6417. * Add a protocol to the provides section
  6418. * @param string protocol type
  6419. * @param string protocol version
  6420. * @param string protocol name, if any
  6421. * @param string mirror name, if this is a mirror's protocol
  6422. * @return bool
  6423. */
  6424. function addFunction($type, $version, $name = '', $mirror = false)
  6425. {
  6426. if ($mirror) {
  6427. return $this->addMirrorFunction($mirror, $type, $version, $name);
  6428. }
  6429. $set = array('attribs' => array('version' => $version), '_content' => $name);
  6430. if (!isset($this->_channelInfo['servers']['primary'][$type]['function'])) {
  6431. if (!isset($this->_channelInfo['servers'])) {
  6432. $this->_channelInfo['servers'] = array('primary' =>
  6433. array($type => array()));
  6434. } elseif (!isset($this->_channelInfo['servers']['primary'])) {
  6435. $this->_channelInfo['servers']['primary'] = array($type => array());
  6436. }
  6437. $this->_channelInfo['servers']['primary'][$type]['function'] = $set;
  6438. $this->_isValid = false;
  6439. return true;
  6440. } elseif (!isset($this->_channelInfo['servers']['primary'][$type]['function'][0])) {
  6441. $this->_channelInfo['servers']['primary'][$type]['function'] = array(
  6442. $this->_channelInfo['servers']['primary'][$type]['function']);
  6443. }
  6444. $this->_channelInfo['servers']['primary'][$type]['function'][] = $set;
  6445. return true;
  6446. }
  6447. /**
  6448. * Add a protocol to a mirror's provides section
  6449. * @param string mirror name (server)
  6450. * @param string protocol type
  6451. * @param string protocol version
  6452. * @param string protocol name, if any
  6453. */
  6454. function addMirrorFunction($mirror, $type, $version, $name = '')
  6455. {
  6456. if (!isset($this->_channelInfo['servers']['mirror'])) {
  6457. $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  6458. array('mirror' => $mirror));
  6459. return false;
  6460. }
  6461. $setmirror = false;
  6462. if (isset($this->_channelInfo['servers']['mirror'][0])) {
  6463. foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  6464. if ($mirror == $mir['attribs']['host']) {
  6465. $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
  6466. break;
  6467. }
  6468. }
  6469. } else {
  6470. if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
  6471. $setmirror = &$this->_channelInfo['servers']['mirror'];
  6472. }
  6473. }
  6474. if (!$setmirror) {
  6475. $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  6476. array('mirror' => $mirror));
  6477. return false;
  6478. }
  6479. $set = array('attribs' => array('version' => $version), '_content' => $name);
  6480. if (!isset($setmirror[$type]['function'])) {
  6481. $setmirror[$type]['function'] = $set;
  6482. $this->_isValid = false;
  6483. return true;
  6484. } elseif (!isset($setmirror[$type]['function'][0])) {
  6485. $setmirror[$type]['function'] = array($setmirror[$type]['function']);
  6486. }
  6487. $setmirror[$type]['function'][] = $set;
  6488. $this->_isValid = false;
  6489. return true;
  6490. }
  6491. /**
  6492. * @param string Resource Type this url links to
  6493. * @param string URL
  6494. * @param string|false mirror name, if this is not a primary server REST base URL
  6495. */
  6496. function setBaseURL($resourceType, $url, $mirror = false)
  6497. {
  6498. if ($mirror) {
  6499. if (!isset($this->_channelInfo['servers']['mirror'])) {
  6500. $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  6501. array('mirror' => $mirror));
  6502. return false;
  6503. }
  6504. $setmirror = false;
  6505. if (isset($this->_channelInfo['servers']['mirror'][0])) {
  6506. foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  6507. if ($mirror == $mir['attribs']['host']) {
  6508. $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
  6509. break;
  6510. }
  6511. }
  6512. } else {
  6513. if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
  6514. $setmirror = &$this->_channelInfo['servers']['mirror'];
  6515. }
  6516. }
  6517. } else {
  6518. $setmirror = &$this->_channelInfo['servers']['primary'];
  6519. }
  6520. $set = array('attribs' => array('type' => $resourceType), '_content' => $url);
  6521. if (!isset($setmirror['rest'])) {
  6522. $setmirror['rest'] = array();
  6523. }
  6524. if (!isset($setmirror['rest']['baseurl'])) {
  6525. $setmirror['rest']['baseurl'] = $set;
  6526. $this->_isValid = false;
  6527. return true;
  6528. } elseif (!isset($setmirror['rest']['baseurl'][0])) {
  6529. $setmirror['rest']['baseurl'] = array($setmirror['rest']['baseurl']);
  6530. }
  6531. foreach ($setmirror['rest']['baseurl'] as $i => $url) {
  6532. if ($url['attribs']['type'] == $resourceType) {
  6533. $this->_isValid = false;
  6534. $setmirror['rest']['baseurl'][$i] = $set;
  6535. return true;
  6536. }
  6537. }
  6538. $setmirror['rest']['baseurl'][] = $set;
  6539. $this->_isValid = false;
  6540. return true;
  6541. }
  6542. /**
  6543. * @param string mirror server
  6544. * @param int mirror http port
  6545. * @return boolean
  6546. */
  6547. function addMirror($server, $port = null)
  6548. {
  6549. if ($this->_channelInfo['name'] == '__uri') {
  6550. return false; // the __uri channel cannot have mirrors by definition
  6551. }
  6552. $set = array('attribs' => array('host' => $server));
  6553. if (is_numeric($port)) {
  6554. $set['attribs']['port'] = $port;
  6555. }
  6556. if (!isset($this->_channelInfo['servers']['mirror'])) {
  6557. $this->_channelInfo['servers']['mirror'] = $set;
  6558. return true;
  6559. }
  6560. if (!isset($this->_channelInfo['servers']['mirror'][0])) {
  6561. $this->_channelInfo['servers']['mirror'] =
  6562. array($this->_channelInfo['servers']['mirror']);
  6563. }
  6564. $this->_channelInfo['servers']['mirror'][] = $set;
  6565. return true;
  6566. }
  6567. /**
  6568. * Retrieve the name of the validation package for this channel
  6569. * @return string|false
  6570. */
  6571. function getValidationPackage()
  6572. {
  6573. if (!$this->_isValid && !$this->validate()) {
  6574. return false;
  6575. }
  6576. if (!isset($this->_channelInfo['validatepackage'])) {
  6577. return array('attribs' => array('version' => 'default'),
  6578. '_content' => 'PEAR_Validate');
  6579. }
  6580. return $this->_channelInfo['validatepackage'];
  6581. }
  6582. /**
  6583. * Retrieve the object that can be used for custom validation
  6584. * @param string|false the name of the package to validate. If the package is
  6585. * the channel validation package, PEAR_Validate is returned
  6586. * @return PEAR_Validate|false false is returned if the validation package
  6587. * cannot be located
  6588. */
  6589. function &getValidationObject($package = false)
  6590. {
  6591. if (!class_exists('PEAR_Validate')) {
  6592. require_once 'phar://go-pear.phar/' . 'PEAR/Validate.php';
  6593. }
  6594. if (!$this->_isValid) {
  6595. if (!$this->validate()) {
  6596. $a = false;
  6597. return $a;
  6598. }
  6599. }
  6600. if (isset($this->_channelInfo['validatepackage'])) {
  6601. if ($package == $this->_channelInfo['validatepackage']) {
  6602. // channel validation packages are always validated by PEAR_Validate
  6603. $val = new PEAR_Validate;
  6604. return $val;
  6605. }
  6606. if (!class_exists(str_replace('.', '_',
  6607. $this->_channelInfo['validatepackage']['_content']))) {
  6608. if ($this->isIncludeable(str_replace('_', '/',
  6609. $this->_channelInfo['validatepackage']['_content']) . '.php')) {
  6610. include_once 'phar://go-pear.phar/' . str_replace('_', '/',
  6611. $this->_channelInfo['validatepackage']['_content']) . '.php';
  6612. $vclass = str_replace('.', '_',
  6613. $this->_channelInfo['validatepackage']['_content']);
  6614. $val = new $vclass;
  6615. } else {
  6616. $a = false;
  6617. return $a;
  6618. }
  6619. } else {
  6620. $vclass = str_replace('.', '_',
  6621. $this->_channelInfo['validatepackage']['_content']);
  6622. $val = new $vclass;
  6623. }
  6624. } else {
  6625. $val = new PEAR_Validate;
  6626. }
  6627. return $val;
  6628. }
  6629. function isIncludeable($path)
  6630. {
  6631. $possibilities = explode(PATH_SEPARATOR, ini_get('include_path'));
  6632. foreach ($possibilities as $dir) {
  6633. if (file_exists($dir . DIRECTORY_SEPARATOR . $path)
  6634. && is_readable($dir . DIRECTORY_SEPARATOR . $path)) {
  6635. return true;
  6636. }
  6637. }
  6638. return false;
  6639. }
  6640. /**
  6641. * This function is used by the channel updater and retrieves a value set by
  6642. * the registry, or the current time if it has not been set
  6643. * @return string
  6644. */
  6645. function lastModified()
  6646. {
  6647. if (isset($this->_channelInfo['_lastmodified'])) {
  6648. return $this->_channelInfo['_lastmodified'];
  6649. }
  6650. return time();
  6651. }
  6652. }
  6653. <?php
  6654. /**
  6655. * PEAR_ChannelFile_Parser for parsing channel.xml
  6656. *
  6657. * PHP versions 4 and 5
  6658. *
  6659. * @category pear
  6660. * @package PEAR
  6661. * @author Greg Beaver <cellog@php.net>
  6662. * @copyright 1997-2009 The Authors
  6663. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  6664. * @link http://pear.php.net/package/PEAR
  6665. * @since File available since Release 1.4.0a1
  6666. */
  6667. /**
  6668. * base xml parser class
  6669. */
  6670. require_once 'phar://go-pear.phar/' . 'PEAR/XMLParser.php';
  6671. require_once 'phar://go-pear.phar/' . 'PEAR/ChannelFile.php';
  6672. /**
  6673. * Parser for channel.xml
  6674. * @category pear
  6675. * @package PEAR
  6676. * @author Greg Beaver <cellog@php.net>
  6677. * @copyright 1997-2009 The Authors
  6678. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  6679. * @version Release: 1.10.16
  6680. * @link http://pear.php.net/package/PEAR
  6681. * @since Class available since Release 1.4.0a1
  6682. */
  6683. class PEAR_ChannelFile_Parser extends PEAR_XMLParser
  6684. {
  6685. var $_config;
  6686. var $_logger;
  6687. var $_registry;
  6688. function setConfig(&$c)
  6689. {
  6690. $this->_config = &$c;
  6691. $this->_registry = &$c->getRegistry();
  6692. }
  6693. function setLogger(&$l)
  6694. {
  6695. $this->_logger = &$l;
  6696. }
  6697. function parse($data, $file)
  6698. {
  6699. if (PEAR::isError($err = parent::parse($data, $file))) {
  6700. return $err;
  6701. }
  6702. $ret = new PEAR_ChannelFile;
  6703. $ret->setConfig($this->_config);
  6704. if (isset($this->_logger)) {
  6705. $ret->setLogger($this->_logger);
  6706. }
  6707. $ret->fromArray($this->_unserializedData);
  6708. // make sure the filelist is in the easy to read format needed
  6709. $ret->flattenFilelist();
  6710. $ret->setPackagefile($file, $archive);
  6711. return $ret;
  6712. }
  6713. }<?php
  6714. /**
  6715. * PEAR_Command, command pattern class
  6716. *
  6717. * PHP versions 4 and 5
  6718. *
  6719. * @category pear
  6720. * @package PEAR
  6721. * @author Stig Bakken <ssb@php.net>
  6722. * @author Greg Beaver <cellog@php.net>
  6723. * @copyright 1997-2009 The Authors
  6724. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  6725. * @link http://pear.php.net/package/PEAR
  6726. * @since File available since Release 0.1
  6727. */
  6728. /**
  6729. * Needed for error handling
  6730. */
  6731. require_once 'phar://go-pear.phar/' . 'PEAR.php';
  6732. require_once 'phar://go-pear.phar/' . 'PEAR/Frontend.php';
  6733. require_once 'phar://go-pear.phar/' . 'PEAR/XMLParser.php';
  6734. /**
  6735. * List of commands and what classes they are implemented in.
  6736. * @var array command => implementing class
  6737. */
  6738. $GLOBALS['_PEAR_Command_commandlist'] = array();
  6739. /**
  6740. * List of commands and their descriptions
  6741. * @var array command => description
  6742. */
  6743. $GLOBALS['_PEAR_Command_commanddesc'] = array();
  6744. /**
  6745. * List of shortcuts to common commands.
  6746. * @var array shortcut => command
  6747. */
  6748. $GLOBALS['_PEAR_Command_shortcuts'] = array();
  6749. /**
  6750. * Array of command objects
  6751. * @var array class => object
  6752. */
  6753. $GLOBALS['_PEAR_Command_objects'] = array();
  6754. /**
  6755. * PEAR command class, a simple factory class for administrative
  6756. * commands.
  6757. *
  6758. * How to implement command classes:
  6759. *
  6760. * - The class must be called PEAR_Command_Nnn, installed in the
  6761. * "PEAR/Common" subdir, with a method called getCommands() that
  6762. * returns an array of the commands implemented by the class (see
  6763. * PEAR/Command/Install.php for an example).
  6764. *
  6765. * - The class must implement a run() function that is called with three
  6766. * params:
  6767. *
  6768. * (string) command name
  6769. * (array) assoc array with options, freely defined by each
  6770. * command, for example:
  6771. * array('force' => true)
  6772. * (array) list of the other parameters
  6773. *
  6774. * The run() function returns a PEAR_CommandResponse object. Use
  6775. * these methods to get information:
  6776. *
  6777. * int getStatus() Returns PEAR_COMMAND_(SUCCESS|FAILURE|PARTIAL)
  6778. * *_PARTIAL means that you need to issue at least
  6779. * one more command to complete the operation
  6780. * (used for example for validation steps).
  6781. *
  6782. * string getMessage() Returns a message for the user. Remember,
  6783. * no HTML or other interface-specific markup.
  6784. *
  6785. * If something unexpected happens, run() returns a PEAR error.
  6786. *
  6787. * - DON'T OUTPUT ANYTHING! Return text for output instead.
  6788. *
  6789. * - DON'T USE HTML! The text you return will be used from both Gtk,
  6790. * web and command-line interfaces, so for now, keep everything to
  6791. * plain text.
  6792. *
  6793. * - DON'T USE EXIT OR DIE! Always use pear errors. From static
  6794. * classes do PEAR::raiseError(), from other classes do
  6795. * $this->raiseError().
  6796. * @category pear
  6797. * @package PEAR
  6798. * @author Stig Bakken <ssb@php.net>
  6799. * @author Greg Beaver <cellog@php.net>
  6800. * @copyright 1997-2009 The Authors
  6801. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  6802. * @version Release: 1.10.16
  6803. * @link http://pear.php.net/package/PEAR
  6804. * @since Class available since Release 0.1
  6805. */
  6806. class PEAR_Command
  6807. {
  6808. // {{{ factory()
  6809. /**
  6810. * Get the right object for executing a command.
  6811. *
  6812. * @param string $command The name of the command
  6813. * @param object $config Instance of PEAR_Config object
  6814. *
  6815. * @return object the command object or a PEAR error
  6816. */
  6817. public static function &factory($command, &$config)
  6818. {
  6819. if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
  6820. PEAR_Command::registerCommands();
  6821. }
  6822. if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
  6823. $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
  6824. }
  6825. if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
  6826. $a = PEAR::raiseError("unknown command `$command'");
  6827. return $a;
  6828. }
  6829. $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
  6830. if (!class_exists($class)) {
  6831. require_once $GLOBALS['_PEAR_Command_objects'][$class];
  6832. }
  6833. if (!class_exists($class)) {
  6834. $a = PEAR::raiseError("unknown command `$command'");
  6835. return $a;
  6836. }
  6837. $ui =& PEAR_Command::getFrontendObject();
  6838. $obj = new $class($ui, $config);
  6839. return $obj;
  6840. }
  6841. // }}}
  6842. // {{{ & getObject()
  6843. public static function &getObject($command)
  6844. {
  6845. $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
  6846. if (!class_exists($class)) {
  6847. require_once $GLOBALS['_PEAR_Command_objects'][$class];
  6848. }
  6849. if (!class_exists($class)) {
  6850. return PEAR::raiseError("unknown command `$command'");
  6851. }
  6852. $ui =& PEAR_Command::getFrontendObject();
  6853. $config = &PEAR_Config::singleton();
  6854. $obj = new $class($ui, $config);
  6855. return $obj;
  6856. }
  6857. // }}}
  6858. // {{{ & getFrontendObject()
  6859. /**
  6860. * Get instance of frontend object.
  6861. *
  6862. * @return object|PEAR_Error
  6863. */
  6864. public static function &getFrontendObject()
  6865. {
  6866. $a = &PEAR_Frontend::singleton();
  6867. return $a;
  6868. }
  6869. // }}}
  6870. // {{{ & setFrontendClass()
  6871. /**
  6872. * Load current frontend class.
  6873. *
  6874. * @param string $uiclass Name of class implementing the frontend
  6875. *
  6876. * @return object the frontend object, or a PEAR error
  6877. */
  6878. public static function &setFrontendClass($uiclass)
  6879. {
  6880. $a = &PEAR_Frontend::setFrontendClass($uiclass);
  6881. return $a;
  6882. }
  6883. // }}}
  6884. // {{{ setFrontendType()
  6885. /**
  6886. * Set current frontend.
  6887. *
  6888. * @param string $uitype Name of the frontend type (for example "CLI")
  6889. *
  6890. * @return object the frontend object, or a PEAR error
  6891. */
  6892. public static function setFrontendType($uitype)
  6893. {
  6894. $uiclass = 'PEAR_Frontend_' . $uitype;
  6895. return PEAR_Command::setFrontendClass($uiclass);
  6896. }
  6897. // }}}
  6898. // {{{ registerCommands()
  6899. /**
  6900. * Scan through the Command directory looking for classes
  6901. * and see what commands they implement.
  6902. *
  6903. * @param bool (optional) if FALSE (default), the new list of
  6904. * commands should replace the current one. If TRUE,
  6905. * new entries will be merged with old.
  6906. *
  6907. * @param string (optional) where (what directory) to look for
  6908. * classes, defaults to the Command subdirectory of
  6909. * the directory from where this file (__FILE__) is
  6910. * included.
  6911. *
  6912. * @return bool TRUE on success, a PEAR error on failure
  6913. */
  6914. public static function registerCommands($merge = false, $dir = null)
  6915. {
  6916. $parser = new PEAR_XMLParser;
  6917. if ($dir === null) {
  6918. $dir = dirname(__FILE__) . '/Command';
  6919. }
  6920. if (!is_dir($dir)) {
  6921. return PEAR::raiseError("registerCommands: opendir($dir) '$dir' does not exist or is not a directory");
  6922. }
  6923. $dp = @opendir($dir);
  6924. if (empty($dp)) {
  6925. return PEAR::raiseError("registerCommands: opendir($dir) failed");
  6926. }
  6927. if (!$merge) {
  6928. $GLOBALS['_PEAR_Command_commandlist'] = array();
  6929. }
  6930. while ($file = readdir($dp)) {
  6931. if ($file[0] == '.' || substr($file, -4) != '.xml') {
  6932. continue;
  6933. }
  6934. $f = substr($file, 0, -4);
  6935. $class = "PEAR_Command_" . $f;
  6936. // List of commands
  6937. if (empty($GLOBALS['_PEAR_Command_objects'][$class])) {
  6938. $GLOBALS['_PEAR_Command_objects'][$class] = "$dir/" . $f . '.php';
  6939. }
  6940. $parser->parse(file_get_contents("$dir/$file"));
  6941. $implements = $parser->getData();
  6942. foreach ($implements as $command => $desc) {
  6943. if ($command == 'attribs') {
  6944. continue;
  6945. }
  6946. if (isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
  6947. return PEAR::raiseError('Command "' . $command . '" already registered in ' .
  6948. 'class "' . $GLOBALS['_PEAR_Command_commandlist'][$command] . '"');
  6949. }
  6950. $GLOBALS['_PEAR_Command_commandlist'][$command] = $class;
  6951. $GLOBALS['_PEAR_Command_commanddesc'][$command] = $desc['summary'];
  6952. if (isset($desc['shortcut'])) {
  6953. $shortcut = $desc['shortcut'];
  6954. if (isset($GLOBALS['_PEAR_Command_shortcuts'][$shortcut])) {
  6955. return PEAR::raiseError('Command shortcut "' . $shortcut . '" already ' .
  6956. 'registered to command "' . $command . '" in class "' .
  6957. $GLOBALS['_PEAR_Command_commandlist'][$command] . '"');
  6958. }
  6959. $GLOBALS['_PEAR_Command_shortcuts'][$shortcut] = $command;
  6960. }
  6961. if (isset($desc['options']) && $desc['options']) {
  6962. foreach ($desc['options'] as $oname => $option) {
  6963. if (isset($option['shortopt']) && strlen($option['shortopt']) > 1) {
  6964. return PEAR::raiseError('Option "' . $oname . '" short option "' .
  6965. $option['shortopt'] . '" must be ' .
  6966. 'only 1 character in Command "' . $command . '" in class "' .
  6967. $class . '"');
  6968. }
  6969. }
  6970. }
  6971. }
  6972. }
  6973. ksort($GLOBALS['_PEAR_Command_shortcuts']);
  6974. ksort($GLOBALS['_PEAR_Command_commandlist']);
  6975. @closedir($dp);
  6976. return true;
  6977. }
  6978. // }}}
  6979. // {{{ getCommands()
  6980. /**
  6981. * Get the list of currently supported commands, and what
  6982. * classes implement them.
  6983. *
  6984. * @return array command => implementing class
  6985. */
  6986. public static function getCommands()
  6987. {
  6988. if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
  6989. PEAR_Command::registerCommands();
  6990. }
  6991. return $GLOBALS['_PEAR_Command_commandlist'];
  6992. }
  6993. // }}}
  6994. // {{{ getShortcuts()
  6995. /**
  6996. * Get the list of command shortcuts.
  6997. *
  6998. * @return array shortcut => command
  6999. */
  7000. public static function getShortcuts()
  7001. {
  7002. if (empty($GLOBALS['_PEAR_Command_shortcuts'])) {
  7003. PEAR_Command::registerCommands();
  7004. }
  7005. return $GLOBALS['_PEAR_Command_shortcuts'];
  7006. }
  7007. // }}}
  7008. // {{{ getGetoptArgs()
  7009. /**
  7010. * Compiles arguments for getopt.
  7011. *
  7012. * @param string $command command to get optstring for
  7013. * @param string $short_args (reference) short getopt format
  7014. * @param array $long_args (reference) long getopt format
  7015. *
  7016. * @return void
  7017. */
  7018. public static function getGetoptArgs($command, &$short_args, &$long_args)
  7019. {
  7020. if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
  7021. PEAR_Command::registerCommands();
  7022. }
  7023. if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
  7024. $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
  7025. }
  7026. if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
  7027. return null;
  7028. }
  7029. $obj = &PEAR_Command::getObject($command);
  7030. return $obj->getGetoptArgs($command, $short_args, $long_args);
  7031. }
  7032. // }}}
  7033. // {{{ getDescription()
  7034. /**
  7035. * Get description for a command.
  7036. *
  7037. * @param string $command Name of the command
  7038. *
  7039. * @return string command description
  7040. */
  7041. public static function getDescription($command)
  7042. {
  7043. if (!isset($GLOBALS['_PEAR_Command_commanddesc'][$command])) {
  7044. return null;
  7045. }
  7046. return $GLOBALS['_PEAR_Command_commanddesc'][$command];
  7047. }
  7048. // }}}
  7049. // {{{ getHelp()
  7050. /**
  7051. * Get help for command.
  7052. *
  7053. * @param string $command Name of the command to return help for
  7054. */
  7055. public static function getHelp($command)
  7056. {
  7057. $cmds = PEAR_Command::getCommands();
  7058. if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
  7059. $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
  7060. }
  7061. if (isset($cmds[$command])) {
  7062. $obj = &PEAR_Command::getObject($command);
  7063. return $obj->getHelp($command);
  7064. }
  7065. return false;
  7066. }
  7067. // }}}
  7068. }<?php
  7069. /**
  7070. * PEAR_Command_Common base class
  7071. *
  7072. * PHP versions 4 and 5
  7073. *
  7074. * @category pear
  7075. * @package PEAR
  7076. * @author Stig Bakken <ssb@php.net>
  7077. * @author Greg Beaver <cellog@php.net>
  7078. * @copyright 1997-2009 The Authors
  7079. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  7080. * @link http://pear.php.net/package/PEAR
  7081. * @since File available since Release 0.1
  7082. */
  7083. /**
  7084. * base class
  7085. */
  7086. require_once 'phar://go-pear.phar/' . 'PEAR.php';
  7087. /**
  7088. * PEAR commands base class
  7089. *
  7090. * @category pear
  7091. * @package PEAR
  7092. * @author Stig Bakken <ssb@php.net>
  7093. * @author Greg Beaver <cellog@php.net>
  7094. * @copyright 1997-2009 The Authors
  7095. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  7096. * @version Release: 1.10.16
  7097. * @link http://pear.php.net/package/PEAR
  7098. * @since Class available since Release 0.1
  7099. */
  7100. class PEAR_Command_Common extends PEAR
  7101. {
  7102. /**
  7103. * PEAR_Config object used to pass user system and configuration
  7104. * on when executing commands
  7105. *
  7106. * @var PEAR_Config
  7107. */
  7108. var $config;
  7109. /**
  7110. * @var PEAR_Registry
  7111. * @access protected
  7112. */
  7113. var $_registry;
  7114. /**
  7115. * User Interface object, for all interaction with the user.
  7116. * @var object
  7117. */
  7118. var $ui;
  7119. var $_deps_rel_trans = array(
  7120. 'lt' => '<',
  7121. 'le' => '<=',
  7122. 'eq' => '=',
  7123. 'ne' => '!=',
  7124. 'gt' => '>',
  7125. 'ge' => '>=',
  7126. 'has' => '=='
  7127. );
  7128. var $_deps_type_trans = array(
  7129. 'pkg' => 'package',
  7130. 'ext' => 'extension',
  7131. 'php' => 'PHP',
  7132. 'prog' => 'external program',
  7133. 'ldlib' => 'external library for linking',
  7134. 'rtlib' => 'external runtime library',
  7135. 'os' => 'operating system',
  7136. 'websrv' => 'web server',
  7137. 'sapi' => 'SAPI backend'
  7138. );
  7139. /**
  7140. * PEAR_Command_Common constructor.
  7141. *
  7142. * @access public
  7143. */
  7144. function __construct(&$ui, &$config)
  7145. {
  7146. parent::__construct();
  7147. $this->config = &$config;
  7148. $this->ui = &$ui;
  7149. }
  7150. /**
  7151. * Return a list of all the commands defined by this class.
  7152. * @return array list of commands
  7153. * @access public
  7154. */
  7155. function getCommands()
  7156. {
  7157. $ret = array();
  7158. foreach (array_keys($this->commands) as $command) {
  7159. $ret[$command] = $this->commands[$command]['summary'];
  7160. }
  7161. return $ret;
  7162. }
  7163. /**
  7164. * Return a list of all the command shortcuts defined by this class.
  7165. * @return array shortcut => command
  7166. * @access public
  7167. */
  7168. function getShortcuts()
  7169. {
  7170. $ret = array();
  7171. foreach (array_keys($this->commands) as $command) {
  7172. if (isset($this->commands[$command]['shortcut'])) {
  7173. $ret[$this->commands[$command]['shortcut']] = $command;
  7174. }
  7175. }
  7176. return $ret;
  7177. }
  7178. function getOptions($command)
  7179. {
  7180. $shortcuts = $this->getShortcuts();
  7181. if (isset($shortcuts[$command])) {
  7182. $command = $shortcuts[$command];
  7183. }
  7184. if (isset($this->commands[$command]) &&
  7185. isset($this->commands[$command]['options'])) {
  7186. return $this->commands[$command]['options'];
  7187. }
  7188. return null;
  7189. }
  7190. function getGetoptArgs($command, &$short_args, &$long_args)
  7191. {
  7192. $short_args = '';
  7193. $long_args = array();
  7194. if (empty($this->commands[$command]) || empty($this->commands[$command]['options'])) {
  7195. return;
  7196. }
  7197. reset($this->commands[$command]['options']);
  7198. foreach ($this->commands[$command]['options'] as $option => $info) {
  7199. $larg = $sarg = '';
  7200. if (isset($info['arg'])) {
  7201. if ($info['arg'][0] == '(') {
  7202. $larg = '==';
  7203. $sarg = '::';
  7204. $arg = substr($info['arg'], 1, -1);
  7205. } else {
  7206. $larg = '=';
  7207. $sarg = ':';
  7208. $arg = $info['arg'];
  7209. }
  7210. }
  7211. if (isset($info['shortopt'])) {
  7212. $short_args .= $info['shortopt'] . $sarg;
  7213. }
  7214. $long_args[] = $option . $larg;
  7215. }
  7216. }
  7217. /**
  7218. * Returns the help message for the given command
  7219. *
  7220. * @param string $command The command
  7221. * @return mixed A fail string if the command does not have help or
  7222. * a two elements array containing [0]=>help string,
  7223. * [1]=> help string for the accepted cmd args
  7224. */
  7225. function getHelp($command)
  7226. {
  7227. $config = &PEAR_Config::singleton();
  7228. if (!isset($this->commands[$command])) {
  7229. return "No such command \"$command\"";
  7230. }
  7231. $help = null;
  7232. if (isset($this->commands[$command]['doc'])) {
  7233. $help = $this->commands[$command]['doc'];
  7234. }
  7235. if (empty($help)) {
  7236. // XXX (cox) Fallback to summary if there is no doc (show both?)
  7237. if (!isset($this->commands[$command]['summary'])) {
  7238. return "No help for command \"$command\"";
  7239. }
  7240. $help = $this->commands[$command]['summary'];
  7241. }
  7242. if (preg_match_all('/{config\s+([^\}]+)}/', $help, $matches)) {
  7243. foreach($matches[0] as $k => $v) {
  7244. $help = preg_replace("/$v/", $config->get($matches[1][$k]), $help);
  7245. }
  7246. }
  7247. return array($help, $this->getHelpArgs($command));
  7248. }
  7249. /**
  7250. * Returns the help for the accepted arguments of a command
  7251. *
  7252. * @param string $command
  7253. * @return string The help string
  7254. */
  7255. function getHelpArgs($command)
  7256. {
  7257. if (isset($this->commands[$command]['options']) &&
  7258. count($this->commands[$command]['options']))
  7259. {
  7260. $help = "Options:\n";
  7261. foreach ($this->commands[$command]['options'] as $k => $v) {
  7262. if (isset($v['arg'])) {
  7263. if ($v['arg'][0] == '(') {
  7264. $arg = substr($v['arg'], 1, -1);
  7265. $sapp = " [$arg]";
  7266. $lapp = "[=$arg]";
  7267. } else {
  7268. $sapp = " $v[arg]";
  7269. $lapp = "=$v[arg]";
  7270. }
  7271. } else {
  7272. $sapp = $lapp = "";
  7273. }
  7274. if (isset($v['shortopt'])) {
  7275. $s = $v['shortopt'];
  7276. $help .= " -$s$sapp, --$k$lapp\n";
  7277. } else {
  7278. $help .= " --$k$lapp\n";
  7279. }
  7280. $p = " ";
  7281. $doc = rtrim(str_replace("\n", "\n$p", $v['doc']));
  7282. $help .= " $doc\n";
  7283. }
  7284. return $help;
  7285. }
  7286. return null;
  7287. }
  7288. function run($command, $options, $params)
  7289. {
  7290. if (empty($this->commands[$command]['function'])) {
  7291. // look for shortcuts
  7292. foreach (array_keys($this->commands) as $cmd) {
  7293. if (isset($this->commands[$cmd]['shortcut']) && $this->commands[$cmd]['shortcut'] == $command) {
  7294. if (empty($this->commands[$cmd]['function'])) {
  7295. return $this->raiseError("unknown command `$command'");
  7296. } else {
  7297. $func = $this->commands[$cmd]['function'];
  7298. }
  7299. $command = $cmd;
  7300. //$command = $this->commands[$cmd]['function'];
  7301. break;
  7302. }
  7303. }
  7304. } else {
  7305. $func = $this->commands[$command]['function'];
  7306. }
  7307. return $this->$func($command, $options, $params);
  7308. }
  7309. }
  7310. <?php
  7311. /**
  7312. * PEAR_Command_Install (install, upgrade, upgrade-all, uninstall, bundle, run-scripts commands)
  7313. *
  7314. * PHP versions 4 and 5
  7315. *
  7316. * @category pear
  7317. * @package PEAR
  7318. * @author Stig Bakken <ssb@php.net>
  7319. * @author Greg Beaver <cellog@php.net>
  7320. * @copyright 1997-2009 The Authors
  7321. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  7322. * @link http://pear.php.net/package/PEAR
  7323. * @since File available since Release 0.1
  7324. */
  7325. /**
  7326. * base class
  7327. */
  7328. require_once 'phar://go-pear.phar/' . 'PEAR/Command/Common.php';
  7329. /**
  7330. * PEAR commands for installation or deinstallation/upgrading of
  7331. * packages.
  7332. *
  7333. * @category pear
  7334. * @package PEAR
  7335. * @author Stig Bakken <ssb@php.net>
  7336. * @author Greg Beaver <cellog@php.net>
  7337. * @copyright 1997-2009 The Authors
  7338. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  7339. * @version Release: 1.10.16
  7340. * @link http://pear.php.net/package/PEAR
  7341. * @since Class available since Release 0.1
  7342. */
  7343. class PEAR_Command_Install extends PEAR_Command_Common
  7344. {
  7345. // {{{ properties
  7346. var $commands = array(
  7347. 'install' => array(
  7348. 'summary' => 'Install Package',
  7349. 'function' => 'doInstall',
  7350. 'shortcut' => 'i',
  7351. 'options' => array(
  7352. 'force' => array(
  7353. 'shortopt' => 'f',
  7354. 'doc' => 'will overwrite newer installed packages',
  7355. ),
  7356. 'loose' => array(
  7357. 'shortopt' => 'l',
  7358. 'doc' => 'do not check for recommended dependency version',
  7359. ),
  7360. 'nodeps' => array(
  7361. 'shortopt' => 'n',
  7362. 'doc' => 'ignore dependencies, install anyway',
  7363. ),
  7364. 'register-only' => array(
  7365. 'shortopt' => 'r',
  7366. 'doc' => 'do not install files, only register the package as installed',
  7367. ),
  7368. 'soft' => array(
  7369. 'shortopt' => 's',
  7370. 'doc' => 'soft install, fail silently, or upgrade if already installed',
  7371. ),
  7372. 'nobuild' => array(
  7373. 'shortopt' => 'B',
  7374. 'doc' => 'don\'t build C extensions',
  7375. ),
  7376. 'configureoptions' => array(
  7377. 'shortopt' => 'D',
  7378. 'arg' => 'OPTION1=VALUE[ OPTION2=VALUE]',
  7379. 'doc' => 'space-delimited list of configure options',
  7380. ),
  7381. 'nocompress' => array(
  7382. 'shortopt' => 'Z',
  7383. 'doc' => 'request uncompressed files when downloading',
  7384. ),
  7385. 'installroot' => array(
  7386. 'shortopt' => 'R',
  7387. 'arg' => 'DIR',
  7388. 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM',
  7389. ),
  7390. 'packagingroot' => array(
  7391. 'shortopt' => 'P',
  7392. 'arg' => 'DIR',
  7393. 'doc' => 'root directory used when packaging files, like RPM packaging',
  7394. ),
  7395. 'ignore-errors' => array(
  7396. 'doc' => 'force install even if there were errors',
  7397. ),
  7398. 'alldeps' => array(
  7399. 'shortopt' => 'a',
  7400. 'doc' => 'install all required and optional dependencies',
  7401. ),
  7402. 'onlyreqdeps' => array(
  7403. 'shortopt' => 'o',
  7404. 'doc' => 'install all required dependencies',
  7405. ),
  7406. 'offline' => array(
  7407. 'shortopt' => 'O',
  7408. 'doc' => 'do not attempt to download any urls or contact channels',
  7409. ),
  7410. 'pretend' => array(
  7411. 'shortopt' => 'p',
  7412. 'doc' => 'Only list the packages that would be downloaded',
  7413. ),
  7414. ),
  7415. 'doc' => '[channel/]<package> ...
  7416. Installs one or more PEAR packages. You can specify a package to
  7417. install in four ways:
  7418. "Package-1.0.tgz" : installs from a local file
  7419. "http://example.com/Package-1.0.tgz" : installs from
  7420. anywhere on the net.
  7421. "package.xml" : installs the package described in
  7422. package.xml. Useful for testing, or for wrapping a PEAR package in
  7423. another package manager such as RPM.
  7424. "Package[-version/state][.tar]" : queries your default channel\'s server
  7425. ({config master_server}) and downloads the newest package with
  7426. the preferred quality/state ({config preferred_state}).
  7427. To retrieve Package version 1.1, use "Package-1.1," to retrieve
  7428. Package state beta, use "Package-beta." To retrieve an uncompressed
  7429. file, append .tar (make sure there is no file by the same name first)
  7430. To download a package from another channel, prefix with the channel name like
  7431. "channel/Package"
  7432. More than one package may be specified at once. It is ok to mix these
  7433. four ways of specifying packages.
  7434. '),
  7435. 'upgrade' => array(
  7436. 'summary' => 'Upgrade Package',
  7437. 'function' => 'doInstall',
  7438. 'shortcut' => 'up',
  7439. 'options' => array(
  7440. 'channel' => array(
  7441. 'shortopt' => 'c',
  7442. 'doc' => 'upgrade packages from a specific channel',
  7443. 'arg' => 'CHAN',
  7444. ),
  7445. 'force' => array(
  7446. 'shortopt' => 'f',
  7447. 'doc' => 'overwrite newer installed packages',
  7448. ),
  7449. 'loose' => array(
  7450. 'shortopt' => 'l',
  7451. 'doc' => 'do not check for recommended dependency version',
  7452. ),
  7453. 'nodeps' => array(
  7454. 'shortopt' => 'n',
  7455. 'doc' => 'ignore dependencies, upgrade anyway',
  7456. ),
  7457. 'register-only' => array(
  7458. 'shortopt' => 'r',
  7459. 'doc' => 'do not install files, only register the package as upgraded',
  7460. ),
  7461. 'nobuild' => array(
  7462. 'shortopt' => 'B',
  7463. 'doc' => 'don\'t build C extensions',
  7464. ),
  7465. 'nocompress' => array(
  7466. 'shortopt' => 'Z',
  7467. 'doc' => 'request uncompressed files when downloading',
  7468. ),
  7469. 'installroot' => array(
  7470. 'shortopt' => 'R',
  7471. 'arg' => 'DIR',
  7472. 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
  7473. ),
  7474. 'ignore-errors' => array(
  7475. 'doc' => 'force install even if there were errors',
  7476. ),
  7477. 'alldeps' => array(
  7478. 'shortopt' => 'a',
  7479. 'doc' => 'install all required and optional dependencies',
  7480. ),
  7481. 'onlyreqdeps' => array(
  7482. 'shortopt' => 'o',
  7483. 'doc' => 'install all required dependencies',
  7484. ),
  7485. 'offline' => array(
  7486. 'shortopt' => 'O',
  7487. 'doc' => 'do not attempt to download any urls or contact channels',
  7488. ),
  7489. 'pretend' => array(
  7490. 'shortopt' => 'p',
  7491. 'doc' => 'Only list the packages that would be downloaded',
  7492. ),
  7493. ),
  7494. 'doc' => '<package> ...
  7495. Upgrades one or more PEAR packages. See documentation for the
  7496. "install" command for ways to specify a package.
  7497. When upgrading, your package will be updated if the provided new
  7498. package has a higher version number (use the -f option if you need to
  7499. upgrade anyway).
  7500. More than one package may be specified at once.
  7501. '),
  7502. 'upgrade-all' => array(
  7503. 'summary' => 'Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters]',
  7504. 'function' => 'doUpgradeAll',
  7505. 'shortcut' => 'ua',
  7506. 'options' => array(
  7507. 'channel' => array(
  7508. 'shortopt' => 'c',
  7509. 'doc' => 'upgrade packages from a specific channel',
  7510. 'arg' => 'CHAN',
  7511. ),
  7512. 'nodeps' => array(
  7513. 'shortopt' => 'n',
  7514. 'doc' => 'ignore dependencies, upgrade anyway',
  7515. ),
  7516. 'register-only' => array(
  7517. 'shortopt' => 'r',
  7518. 'doc' => 'do not install files, only register the package as upgraded',
  7519. ),
  7520. 'nobuild' => array(
  7521. 'shortopt' => 'B',
  7522. 'doc' => 'don\'t build C extensions',
  7523. ),
  7524. 'nocompress' => array(
  7525. 'shortopt' => 'Z',
  7526. 'doc' => 'request uncompressed files when downloading',
  7527. ),
  7528. 'installroot' => array(
  7529. 'shortopt' => 'R',
  7530. 'arg' => 'DIR',
  7531. 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM',
  7532. ),
  7533. 'ignore-errors' => array(
  7534. 'doc' => 'force install even if there were errors',
  7535. ),
  7536. 'loose' => array(
  7537. 'doc' => 'do not check for recommended dependency version',
  7538. ),
  7539. ),
  7540. 'doc' => '
  7541. WARNING: This function is deprecated in favor of using the upgrade command with no params
  7542. Upgrades all packages that have a newer release available. Upgrades are
  7543. done only if there is a release available of the state specified in
  7544. "preferred_state" (currently {config preferred_state}), or a state considered
  7545. more stable.
  7546. '),
  7547. 'uninstall' => array(
  7548. 'summary' => 'Un-install Package',
  7549. 'function' => 'doUninstall',
  7550. 'shortcut' => 'un',
  7551. 'options' => array(
  7552. 'nodeps' => array(
  7553. 'shortopt' => 'n',
  7554. 'doc' => 'ignore dependencies, uninstall anyway',
  7555. ),
  7556. 'register-only' => array(
  7557. 'shortopt' => 'r',
  7558. 'doc' => 'do not remove files, only register the packages as not installed',
  7559. ),
  7560. 'installroot' => array(
  7561. 'shortopt' => 'R',
  7562. 'arg' => 'DIR',
  7563. 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
  7564. ),
  7565. 'ignore-errors' => array(
  7566. 'doc' => 'force install even if there were errors',
  7567. ),
  7568. 'offline' => array(
  7569. 'shortopt' => 'O',
  7570. 'doc' => 'do not attempt to uninstall remotely',
  7571. ),
  7572. ),
  7573. 'doc' => '[channel/]<package> ...
  7574. Uninstalls one or more PEAR packages. More than one package may be
  7575. specified at once. Prefix with channel name to uninstall from a
  7576. channel not in your default channel ({config default_channel})
  7577. '),
  7578. 'bundle' => array(
  7579. 'summary' => 'Unpacks a Pecl Package',
  7580. 'function' => 'doBundle',
  7581. 'shortcut' => 'bun',
  7582. 'options' => array(
  7583. 'destination' => array(
  7584. 'shortopt' => 'd',
  7585. 'arg' => 'DIR',
  7586. 'doc' => 'Optional destination directory for unpacking (defaults to current path or "ext" if exists)',
  7587. ),
  7588. 'force' => array(
  7589. 'shortopt' => 'f',
  7590. 'doc' => 'Force the unpacking even if there were errors in the package',
  7591. ),
  7592. ),
  7593. 'doc' => '<package>
  7594. Unpacks a Pecl Package into the selected location. It will download the
  7595. package if needed.
  7596. '),
  7597. 'run-scripts' => array(
  7598. 'summary' => 'Run Post-Install Scripts bundled with a package',
  7599. 'function' => 'doRunScripts',
  7600. 'shortcut' => 'rs',
  7601. 'options' => array(
  7602. ),
  7603. 'doc' => '<package>
  7604. Run post-installation scripts in package <package>, if any exist.
  7605. '),
  7606. );
  7607. // }}}
  7608. // {{{ constructor
  7609. /**
  7610. * PEAR_Command_Install constructor.
  7611. *
  7612. * @access public
  7613. */
  7614. function __construct(&$ui, &$config)
  7615. {
  7616. parent::__construct($ui, $config);
  7617. }
  7618. // }}}
  7619. /**
  7620. * For unit testing purposes
  7621. */
  7622. function &getDownloader(&$ui, $options, &$config)
  7623. {
  7624. if (!class_exists('PEAR_Downloader')) {
  7625. require_once 'phar://go-pear.phar/' . 'PEAR/Downloader.php';
  7626. }
  7627. $a = new PEAR_Downloader($ui, $options, $config);
  7628. return $a;
  7629. }
  7630. /**
  7631. * For unit testing purposes
  7632. */
  7633. function &getInstaller(&$ui)
  7634. {
  7635. if (!class_exists('PEAR_Installer')) {
  7636. require_once 'phar://go-pear.phar/' . 'PEAR/Installer.php';
  7637. }
  7638. $a = new PEAR_Installer($ui);
  7639. return $a;
  7640. }
  7641. function enableExtension($binaries, $type)
  7642. {
  7643. if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) {
  7644. return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location');
  7645. }
  7646. $ini = $this->_parseIni($phpini);
  7647. if (PEAR::isError($ini)) {
  7648. return $ini;
  7649. }
  7650. $line = 0;
  7651. if ($type == 'extsrc' || $type == 'extbin') {
  7652. $search = 'extensions';
  7653. $enable = 'extension';
  7654. } else {
  7655. $search = 'zend_extensions';
  7656. ob_start();
  7657. phpinfo(INFO_GENERAL);
  7658. $info = ob_get_contents();
  7659. ob_end_clean();
  7660. $debug = function_exists('leak') ? '_debug' : '';
  7661. $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
  7662. $enable = 'zend_extension' . $debug . $ts;
  7663. }
  7664. foreach ($ini[$search] as $line => $extension) {
  7665. if (in_array($extension, $binaries, true) || in_array(
  7666. $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) {
  7667. // already enabled - assume if one is, all are
  7668. return true;
  7669. }
  7670. }
  7671. if ($line) {
  7672. $newini = array_slice($ini['all'], 0, $line);
  7673. } else {
  7674. $newini = array();
  7675. }
  7676. foreach ($binaries as $binary) {
  7677. if ($ini['extension_dir']) {
  7678. $binary = basename($binary);
  7679. }
  7680. $newini[] = $enable . '="' . $binary . '"' . (OS_UNIX ? "\n" : "\r\n");
  7681. }
  7682. $newini = array_merge($newini, array_slice($ini['all'], $line));
  7683. $fp = @fopen($phpini, 'wb');
  7684. if (!$fp) {
  7685. return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing');
  7686. }
  7687. foreach ($newini as $line) {
  7688. fwrite($fp, $line);
  7689. }
  7690. fclose($fp);
  7691. return true;
  7692. }
  7693. function disableExtension($binaries, $type)
  7694. {
  7695. if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) {
  7696. return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location');
  7697. }
  7698. $ini = $this->_parseIni($phpini);
  7699. if (PEAR::isError($ini)) {
  7700. return $ini;
  7701. }
  7702. $line = 0;
  7703. if ($type == 'extsrc' || $type == 'extbin') {
  7704. $search = 'extensions';
  7705. $enable = 'extension';
  7706. } else {
  7707. $search = 'zend_extensions';
  7708. ob_start();
  7709. phpinfo(INFO_GENERAL);
  7710. $info = ob_get_contents();
  7711. ob_end_clean();
  7712. $debug = function_exists('leak') ? '_debug' : '';
  7713. $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
  7714. $enable = 'zend_extension' . $debug . $ts;
  7715. }
  7716. $found = false;
  7717. foreach ($ini[$search] as $line => $extension) {
  7718. if (in_array($extension, $binaries, true) || in_array(
  7719. $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) {
  7720. $found = true;
  7721. break;
  7722. }
  7723. }
  7724. if (!$found) {
  7725. // not enabled
  7726. return true;
  7727. }
  7728. $fp = @fopen($phpini, 'wb');
  7729. if (!$fp) {
  7730. return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing');
  7731. }
  7732. if ($line) {
  7733. $newini = array_slice($ini['all'], 0, $line);
  7734. // delete the enable line
  7735. $newini = array_merge($newini, array_slice($ini['all'], $line + 1));
  7736. } else {
  7737. $newini = array_slice($ini['all'], 1);
  7738. }
  7739. foreach ($newini as $line) {
  7740. fwrite($fp, $line);
  7741. }
  7742. fclose($fp);
  7743. return true;
  7744. }
  7745. function _parseIni($filename)
  7746. {
  7747. if (!file_exists($filename)) {
  7748. return PEAR::raiseError('php.ini "' . $filename . '" does not exist');
  7749. }
  7750. if (filesize($filename) > 300000) {
  7751. return PEAR::raiseError('php.ini "' . $filename . '" is too large, aborting');
  7752. }
  7753. ob_start();
  7754. phpinfo(INFO_GENERAL);
  7755. $info = ob_get_contents();
  7756. ob_end_clean();
  7757. $debug = function_exists('leak') ? '_debug' : '';
  7758. $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
  7759. $zend_extension_line = 'zend_extension' . $debug . $ts;
  7760. $all = @file($filename);
  7761. if ($all === false) {
  7762. return PEAR::raiseError('php.ini "' . $filename .'" could not be read');
  7763. }
  7764. $zend_extensions = $extensions = array();
  7765. // assume this is right, but pull from the php.ini if it is found
  7766. $extension_dir = ini_get('extension_dir');
  7767. foreach ($all as $linenum => $line) {
  7768. $line = trim($line);
  7769. if (!$line) {
  7770. continue;
  7771. }
  7772. if ($line[0] == ';') {
  7773. continue;
  7774. }
  7775. if (strtolower(substr($line, 0, 13)) == 'extension_dir') {
  7776. $line = trim(substr($line, 13));
  7777. if ($line[0] == '=') {
  7778. $x = trim(substr($line, 1));
  7779. $x = explode(';', $x);
  7780. $extension_dir = str_replace('"', '', array_shift($x));
  7781. continue;
  7782. }
  7783. }
  7784. if (strtolower(substr($line, 0, 9)) == 'extension') {
  7785. $line = trim(substr($line, 9));
  7786. if ($line[0] == '=') {
  7787. $x = trim(substr($line, 1));
  7788. $x = explode(';', $x);
  7789. $extensions[$linenum] = str_replace('"', '', array_shift($x));
  7790. continue;
  7791. }
  7792. }
  7793. if (strtolower(substr($line, 0, strlen($zend_extension_line))) ==
  7794. $zend_extension_line) {
  7795. $line = trim(substr($line, strlen($zend_extension_line)));
  7796. if ($line[0] == '=') {
  7797. $x = trim(substr($line, 1));
  7798. $x = explode(';', $x);
  7799. $zend_extensions[$linenum] = str_replace('"', '', array_shift($x));
  7800. continue;
  7801. }
  7802. }
  7803. }
  7804. return array(
  7805. 'extensions' => $extensions,
  7806. 'zend_extensions' => $zend_extensions,
  7807. 'extension_dir' => $extension_dir,
  7808. 'all' => $all,
  7809. );
  7810. }
  7811. // {{{ doInstall()
  7812. function doInstall($command, $options, $params)
  7813. {
  7814. if (!class_exists('PEAR_PackageFile')) {
  7815. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile.php';
  7816. }
  7817. if (isset($options['installroot']) && isset($options['packagingroot'])) {
  7818. return $this->raiseError('ERROR: cannot use both --installroot and --packagingroot');
  7819. }
  7820. $reg = &$this->config->getRegistry();
  7821. $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel');
  7822. if (!$reg->channelExists($channel)) {
  7823. return $this->raiseError('Channel "' . $channel . '" does not exist');
  7824. }
  7825. if (empty($this->installer)) {
  7826. $this->installer = &$this->getInstaller($this->ui);
  7827. }
  7828. if ($command == 'upgrade' || $command == 'upgrade-all') {
  7829. // If people run the upgrade command but pass nothing, emulate a upgrade-all
  7830. if ($command == 'upgrade' && empty($params)) {
  7831. return $this->doUpgradeAll($command, $options, $params);
  7832. }
  7833. $options['upgrade'] = true;
  7834. } else {
  7835. $packages = $params;
  7836. }
  7837. $instreg = &$reg; // instreg used to check if package is installed
  7838. if (isset($options['packagingroot']) && !isset($options['upgrade'])) {
  7839. $packrootphp_dir = $this->installer->_prependPath(
  7840. $this->config->get('php_dir', null, 'pear.php.net'),
  7841. $options['packagingroot']);
  7842. $metadata_dir = $this->config->get('metadata_dir', null, 'pear.php.net');
  7843. if ($metadata_dir) {
  7844. $metadata_dir = $this->installer->_prependPath(
  7845. $metadata_dir,
  7846. $options['packagingroot']);
  7847. }
  7848. $instreg = new PEAR_Registry($packrootphp_dir, false, false, $metadata_dir); // other instreg!
  7849. if ($this->config->get('verbose') > 2) {
  7850. $this->ui->outputData('using package root: ' . $options['packagingroot']);
  7851. }
  7852. }
  7853. $abstractpackages = $otherpackages = array();
  7854. // parse params
  7855. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  7856. foreach ($params as $param) {
  7857. if (strpos($param, 'http://') === 0) {
  7858. $otherpackages[] = $param;
  7859. continue;
  7860. }
  7861. if (strpos($param, 'channel://') === false && @file_exists($param)) {
  7862. if (isset($options['force'])) {
  7863. $otherpackages[] = $param;
  7864. continue;
  7865. }
  7866. $pkg = new PEAR_PackageFile($this->config);
  7867. $pf = $pkg->fromAnyFile($param, PEAR_VALIDATE_DOWNLOADING);
  7868. if (PEAR::isError($pf)) {
  7869. $otherpackages[] = $param;
  7870. continue;
  7871. }
  7872. $exists = $reg->packageExists($pf->getPackage(), $pf->getChannel());
  7873. $pversion = $reg->packageInfo($pf->getPackage(), 'version', $pf->getChannel());
  7874. $version_compare = version_compare($pf->getVersion(), $pversion, '<=');
  7875. if ($exists && $version_compare) {
  7876. if ($this->config->get('verbose')) {
  7877. $this->ui->outputData('Ignoring installed package ' .
  7878. $reg->parsedPackageNameToString(
  7879. array('package' => $pf->getPackage(),
  7880. 'channel' => $pf->getChannel()), true));
  7881. }
  7882. continue;
  7883. }
  7884. $otherpackages[] = $param;
  7885. continue;
  7886. }
  7887. $e = $reg->parsePackageName($param, $channel);
  7888. if (PEAR::isError($e)) {
  7889. $otherpackages[] = $param;
  7890. } else {
  7891. $abstractpackages[] = $e;
  7892. }
  7893. }
  7894. PEAR::staticPopErrorHandling();
  7895. // if there are any local package .tgz or remote static url, we can't
  7896. // filter. The filter only works for abstract packages
  7897. if (count($abstractpackages) && !isset($options['force'])) {
  7898. // when not being forced, only do necessary upgrades/installs
  7899. if (isset($options['upgrade'])) {
  7900. $abstractpackages = $this->_filterUptodatePackages($abstractpackages, $command);
  7901. } else {
  7902. $count = count($abstractpackages);
  7903. foreach ($abstractpackages as $i => $package) {
  7904. if (isset($package['group'])) {
  7905. // do not filter out install groups
  7906. continue;
  7907. }
  7908. if ($instreg->packageExists($package['package'], $package['channel'])) {
  7909. if ($count > 1) {
  7910. if ($this->config->get('verbose')) {
  7911. $this->ui->outputData('Ignoring installed package ' .
  7912. $reg->parsedPackageNameToString($package, true));
  7913. }
  7914. unset($abstractpackages[$i]);
  7915. } elseif ($count === 1) {
  7916. // Lets try to upgrade it since it's already installed
  7917. $options['upgrade'] = true;
  7918. }
  7919. }
  7920. }
  7921. }
  7922. $abstractpackages =
  7923. array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
  7924. } elseif (count($abstractpackages)) {
  7925. $abstractpackages =
  7926. array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
  7927. }
  7928. $packages = array_merge($abstractpackages, $otherpackages);
  7929. if (!count($packages)) {
  7930. $c = '';
  7931. if (isset($options['channel'])){
  7932. $c .= ' in channel "' . $options['channel'] . '"';
  7933. }
  7934. $this->ui->outputData('Nothing to ' . $command . $c);
  7935. return true;
  7936. }
  7937. $this->downloader = &$this->getDownloader($this->ui, $options, $this->config);
  7938. $errors = $downloaded = $binaries = array();
  7939. $downloaded = &$this->downloader->download($packages);
  7940. if (PEAR::isError($downloaded)) {
  7941. return $this->raiseError($downloaded);
  7942. }
  7943. $errors = $this->downloader->getErrorMsgs();
  7944. if (count($errors)) {
  7945. $err = array();
  7946. $err['data'] = array();
  7947. foreach ($errors as $error) {
  7948. if ($error !== null) {
  7949. $err['data'][] = array($error);
  7950. }
  7951. }
  7952. if (!empty($err['data'])) {
  7953. $err['headline'] = 'Install Errors';
  7954. $this->ui->outputData($err);
  7955. }
  7956. if (!count($downloaded)) {
  7957. return $this->raiseError("$command failed");
  7958. }
  7959. }
  7960. $data = array(
  7961. 'headline' => 'Packages that would be Installed'
  7962. );
  7963. if (isset($options['pretend'])) {
  7964. foreach ($downloaded as $package) {
  7965. $data['data'][] = array($reg->parsedPackageNameToString($package->getParsedPackage()));
  7966. }
  7967. $this->ui->outputData($data, 'pretend');
  7968. return true;
  7969. }
  7970. $this->installer->setOptions($options);
  7971. $this->installer->sortPackagesForInstall($downloaded);
  7972. if (PEAR::isError($err = $this->installer->setDownloadedPackages($downloaded))) {
  7973. $this->raiseError($err->getMessage());
  7974. return true;
  7975. }
  7976. $binaries = $extrainfo = array();
  7977. foreach ($downloaded as $param) {
  7978. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  7979. $info = $this->installer->install($param, $options);
  7980. PEAR::staticPopErrorHandling();
  7981. if (PEAR::isError($info)) {
  7982. $oldinfo = $info;
  7983. $pkg = &$param->getPackageFile();
  7984. if ($info->getCode() != PEAR_INSTALLER_NOBINARY) {
  7985. if (!($info = $pkg->installBinary($this->installer))) {
  7986. return $this->raiseError('ERROR: ' .$oldinfo->getMessage());
  7987. }
  7988. // we just installed a different package than requested,
  7989. // let's change the param and info so that the rest of this works
  7990. $param = $info[0];
  7991. $info = $info[1];
  7992. }
  7993. }
  7994. if (!is_array($info)) {
  7995. return $this->raiseError("$command failed");
  7996. }
  7997. if ($param->getPackageType() == 'extsrc' ||
  7998. $param->getPackageType() == 'extbin' ||
  7999. $param->getPackageType() == 'zendextsrc' ||
  8000. $param->getPackageType() == 'zendextbin'
  8001. ) {
  8002. $pkg = &$param->getPackageFile();
  8003. if ($instbin = $pkg->getInstalledBinary()) {
  8004. $instpkg = &$instreg->getPackage($instbin, $pkg->getChannel());
  8005. } else {
  8006. $instpkg = &$instreg->getPackage($pkg->getPackage(), $pkg->getChannel());
  8007. }
  8008. foreach ($instpkg->getFilelist() as $name => $atts) {
  8009. $pinfo = pathinfo($atts['installed_as']);
  8010. if (!isset($pinfo['extension']) ||
  8011. in_array($pinfo['extension'], array('c', 'h'))
  8012. ) {
  8013. continue; // make sure we don't match php_blah.h
  8014. }
  8015. if ((strpos($pinfo['basename'], 'php_') === 0 &&
  8016. $pinfo['extension'] == 'dll') ||
  8017. // most unices
  8018. $pinfo['extension'] == 'so' ||
  8019. // hp-ux
  8020. $pinfo['extension'] == 'sl') {
  8021. $binaries[] = array($atts['installed_as'], $pinfo);
  8022. break;
  8023. }
  8024. }
  8025. if (count($binaries)) {
  8026. foreach ($binaries as $pinfo) {
  8027. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  8028. $ret = $this->enableExtension(array($pinfo[0]), $param->getPackageType());
  8029. PEAR::staticPopErrorHandling();
  8030. if (PEAR::isError($ret)) {
  8031. $extrainfo[] = $ret->getMessage();
  8032. if ($param->getPackageType() == 'extsrc' ||
  8033. $param->getPackageType() == 'extbin') {
  8034. $exttype = 'extension';
  8035. $extpath = $pinfo[1]['basename'];
  8036. } else {
  8037. $exttype = 'zend_extension';
  8038. $extpath = $atts['installed_as'];
  8039. }
  8040. $extrainfo[] = 'You should add "' . $exttype . '=' .
  8041. $extpath . '" to php.ini';
  8042. } else {
  8043. $extrainfo[] = 'Extension ' . $instpkg->getProvidesExtension() .
  8044. ' enabled in php.ini';
  8045. }
  8046. }
  8047. }
  8048. }
  8049. if ($this->config->get('verbose') > 0) {
  8050. $chan = $param->getChannel();
  8051. $label = $reg->parsedPackageNameToString(
  8052. array(
  8053. 'channel' => $chan,
  8054. 'package' => $param->getPackage(),
  8055. 'version' => $param->getVersion(),
  8056. ));
  8057. $out = array('data' => "$command ok: $label");
  8058. if (isset($info['release_warnings'])) {
  8059. $out['release_warnings'] = $info['release_warnings'];
  8060. }
  8061. $this->ui->outputData($out, $command);
  8062. if (!isset($options['register-only']) && !isset($options['offline'])) {
  8063. if ($this->config->isDefinedLayer('ftp')) {
  8064. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  8065. $info = $this->installer->ftpInstall($param);
  8066. PEAR::staticPopErrorHandling();
  8067. if (PEAR::isError($info)) {
  8068. $this->ui->outputData($info->getMessage());
  8069. $this->ui->outputData("remote install failed: $label");
  8070. } else {
  8071. $this->ui->outputData("remote install ok: $label");
  8072. }
  8073. }
  8074. }
  8075. }
  8076. $deps = $param->getDeps();
  8077. if ($deps) {
  8078. if (isset($deps['group'])) {
  8079. $groups = $deps['group'];
  8080. if (!isset($groups[0])) {
  8081. $groups = array($groups);
  8082. }
  8083. foreach ($groups as $group) {
  8084. if ($group['attribs']['name'] == 'default') {
  8085. // default group is always installed, unless the user
  8086. // explicitly chooses to install another group
  8087. continue;
  8088. }
  8089. $extrainfo[] = $param->getPackage() . ': Optional feature ' .
  8090. $group['attribs']['name'] . ' available (' .
  8091. $group['attribs']['hint'] . ')';
  8092. }
  8093. $extrainfo[] = $param->getPackage() .
  8094. ': To install optional features use "pear install ' .
  8095. $reg->parsedPackageNameToString(
  8096. array('package' => $param->getPackage(),
  8097. 'channel' => $param->getChannel()), true) .
  8098. '#featurename"';
  8099. }
  8100. }
  8101. $pkg = &$instreg->getPackage($param->getPackage(), $param->getChannel());
  8102. // $pkg may be NULL if install is a 'fake' install via --packagingroot
  8103. if (is_object($pkg)) {
  8104. $pkg->setConfig($this->config);
  8105. if ($list = $pkg->listPostinstallScripts()) {
  8106. $pn = $reg->parsedPackageNameToString(array('channel' =>
  8107. $param->getChannel(), 'package' => $param->getPackage()), true);
  8108. $extrainfo[] = $pn . ' has post-install scripts:';
  8109. foreach ($list as $file) {
  8110. $extrainfo[] = $file;
  8111. }
  8112. $extrainfo[] = $param->getPackage() .
  8113. ': Use "pear run-scripts ' . $pn . '" to finish setup.';
  8114. $extrainfo[] = 'DO NOT RUN SCRIPTS FROM UNTRUSTED SOURCES';
  8115. }
  8116. }
  8117. }
  8118. if (count($extrainfo)) {
  8119. foreach ($extrainfo as $info) {
  8120. $this->ui->outputData($info);
  8121. }
  8122. }
  8123. return true;
  8124. }
  8125. // }}}
  8126. // {{{ doUpgradeAll()
  8127. function doUpgradeAll($command, $options, $params)
  8128. {
  8129. $reg = &$this->config->getRegistry();
  8130. $upgrade = array();
  8131. if (isset($options['channel'])) {
  8132. $channels = array($options['channel']);
  8133. } else {
  8134. $channels = $reg->listChannels();
  8135. }
  8136. foreach ($channels as $channel) {
  8137. if ($channel == '__uri') {
  8138. continue;
  8139. }
  8140. // parse name with channel
  8141. foreach ($reg->listPackages($channel) as $name) {
  8142. $upgrade[] = $reg->parsedPackageNameToString(array(
  8143. 'channel' => $channel,
  8144. 'package' => $name
  8145. ));
  8146. }
  8147. }
  8148. $err = $this->doInstall($command, $options, $upgrade);
  8149. if (PEAR::isError($err)) {
  8150. $this->ui->outputData($err->getMessage(), $command);
  8151. }
  8152. }
  8153. // }}}
  8154. // {{{ doUninstall()
  8155. function doUninstall($command, $options, $params)
  8156. {
  8157. if (count($params) < 1) {
  8158. return $this->raiseError("Please supply the package(s) you want to uninstall");
  8159. }
  8160. if (empty($this->installer)) {
  8161. $this->installer = &$this->getInstaller($this->ui);
  8162. }
  8163. if (isset($options['remoteconfig'])) {
  8164. $e = $this->config->readFTPConfigFile($options['remoteconfig']);
  8165. if (!PEAR::isError($e)) {
  8166. $this->installer->setConfig($this->config);
  8167. }
  8168. }
  8169. $reg = &$this->config->getRegistry();
  8170. $newparams = array();
  8171. $binaries = array();
  8172. $badparams = array();
  8173. foreach ($params as $pkg) {
  8174. $channel = $this->config->get('default_channel');
  8175. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  8176. $parsed = $reg->parsePackageName($pkg, $channel);
  8177. PEAR::staticPopErrorHandling();
  8178. if (!$parsed || PEAR::isError($parsed)) {
  8179. $badparams[] = $pkg;
  8180. continue;
  8181. }
  8182. $package = $parsed['package'];
  8183. $channel = $parsed['channel'];
  8184. $info = &$reg->getPackage($package, $channel);
  8185. if ($info === null &&
  8186. ($channel == 'pear.php.net' || $channel == 'pecl.php.net')) {
  8187. // make sure this isn't a package that has flipped from pear to pecl but
  8188. // used a package.xml 1.0
  8189. $testc = ($channel == 'pear.php.net') ? 'pecl.php.net' : 'pear.php.net';
  8190. $info = &$reg->getPackage($package, $testc);
  8191. if ($info !== null) {
  8192. $channel = $testc;
  8193. }
  8194. }
  8195. if ($info === null) {
  8196. $badparams[] = $pkg;
  8197. } else {
  8198. $newparams[] = &$info;
  8199. // check for binary packages (this is an alias for those packages if so)
  8200. if ($installedbinary = $info->getInstalledBinary()) {
  8201. $this->ui->log('adding binary package ' .
  8202. $reg->parsedPackageNameToString(array('channel' => $channel,
  8203. 'package' => $installedbinary), true));
  8204. $newparams[] = &$reg->getPackage($installedbinary, $channel);
  8205. }
  8206. // add the contents of a dependency group to the list of installed packages
  8207. if (isset($parsed['group'])) {
  8208. $group = $info->getDependencyGroup($parsed['group']);
  8209. if ($group) {
  8210. $installed = $reg->getInstalledGroup($group);
  8211. if ($installed) {
  8212. foreach ($installed as $i => $p) {
  8213. $newparams[] = &$installed[$i];
  8214. }
  8215. }
  8216. }
  8217. }
  8218. }
  8219. }
  8220. $err = $this->installer->sortPackagesForUninstall($newparams);
  8221. if (PEAR::isError($err)) {
  8222. $this->ui->outputData($err->getMessage(), $command);
  8223. return true;
  8224. }
  8225. $params = $newparams;
  8226. // twist this to use it to check on whether dependent packages are also being uninstalled
  8227. // for circular dependencies like subpackages
  8228. $this->installer->setUninstallPackages($newparams);
  8229. $params = array_merge($params, $badparams);
  8230. $binaries = array();
  8231. foreach ($params as $pkg) {
  8232. $this->installer->pushErrorHandling(PEAR_ERROR_RETURN);
  8233. if ($err = $this->installer->uninstall($pkg, $options)) {
  8234. $this->installer->popErrorHandling();
  8235. if (PEAR::isError($err)) {
  8236. $this->ui->outputData($err->getMessage(), $command);
  8237. continue;
  8238. }
  8239. if ($pkg->getPackageType() == 'extsrc' ||
  8240. $pkg->getPackageType() == 'extbin' ||
  8241. $pkg->getPackageType() == 'zendextsrc' ||
  8242. $pkg->getPackageType() == 'zendextbin') {
  8243. if ($instbin = $pkg->getInstalledBinary()) {
  8244. continue; // this will be uninstalled later
  8245. }
  8246. foreach ($pkg->getFilelist() as $name => $atts) {
  8247. $pinfo = pathinfo($atts['installed_as']);
  8248. if (!isset($pinfo['extension']) ||
  8249. in_array($pinfo['extension'], array('c', 'h'))) {
  8250. continue; // make sure we don't match php_blah.h
  8251. }
  8252. if ((strpos($pinfo['basename'], 'php_') === 0 &&
  8253. $pinfo['extension'] == 'dll') ||
  8254. // most unices
  8255. $pinfo['extension'] == 'so' ||
  8256. // hp-ux
  8257. $pinfo['extension'] == 'sl') {
  8258. $binaries[] = array($atts['installed_as'], $pinfo);
  8259. break;
  8260. }
  8261. }
  8262. if (count($binaries)) {
  8263. foreach ($binaries as $pinfo) {
  8264. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  8265. $ret = $this->disableExtension(array($pinfo[0]), $pkg->getPackageType());
  8266. PEAR::staticPopErrorHandling();
  8267. if (PEAR::isError($ret)) {
  8268. $extrainfo[] = $ret->getMessage();
  8269. if ($pkg->getPackageType() == 'extsrc' ||
  8270. $pkg->getPackageType() == 'extbin') {
  8271. $exttype = 'extension';
  8272. } else {
  8273. ob_start();
  8274. phpinfo(INFO_GENERAL);
  8275. $info = ob_get_contents();
  8276. ob_end_clean();
  8277. $debug = function_exists('leak') ? '_debug' : '';
  8278. $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
  8279. $exttype = 'zend_extension' . $debug . $ts;
  8280. }
  8281. $this->ui->outputData('Unable to remove "' . $exttype . '=' .
  8282. $pinfo[1]['basename'] . '" from php.ini', $command);
  8283. } else {
  8284. $this->ui->outputData('Extension ' . $pkg->getProvidesExtension() .
  8285. ' disabled in php.ini', $command);
  8286. }
  8287. }
  8288. }
  8289. }
  8290. $savepkg = $pkg;
  8291. if ($this->config->get('verbose') > 0) {
  8292. if (is_object($pkg)) {
  8293. $pkg = $reg->parsedPackageNameToString($pkg);
  8294. }
  8295. $this->ui->outputData("uninstall ok: $pkg", $command);
  8296. }
  8297. if (!isset($options['offline']) && is_object($savepkg) &&
  8298. defined('PEAR_REMOTEINSTALL_OK')) {
  8299. if ($this->config->isDefinedLayer('ftp')) {
  8300. $this->installer->pushErrorHandling(PEAR_ERROR_RETURN);
  8301. $info = $this->installer->ftpUninstall($savepkg);
  8302. $this->installer->popErrorHandling();
  8303. if (PEAR::isError($info)) {
  8304. $this->ui->outputData($info->getMessage());
  8305. $this->ui->outputData("remote uninstall failed: $pkg");
  8306. } else {
  8307. $this->ui->outputData("remote uninstall ok: $pkg");
  8308. }
  8309. }
  8310. }
  8311. } else {
  8312. $this->installer->popErrorHandling();
  8313. if (!is_object($pkg)) {
  8314. return $this->raiseError("uninstall failed: $pkg");
  8315. }
  8316. $pkg = $reg->parsedPackageNameToString($pkg);
  8317. }
  8318. }
  8319. return true;
  8320. }
  8321. // }}}
  8322. // }}}
  8323. // {{{ doBundle()
  8324. /*
  8325. (cox) It just downloads and untars the package, does not do
  8326. any check that the PEAR_Installer::_installFile() does.
  8327. */
  8328. function doBundle($command, $options, $params)
  8329. {
  8330. $opts = array(
  8331. 'force' => true,
  8332. 'nodeps' => true,
  8333. 'soft' => true,
  8334. 'downloadonly' => true
  8335. );
  8336. $downloader = &$this->getDownloader($this->ui, $opts, $this->config);
  8337. $reg = &$this->config->getRegistry();
  8338. if (count($params) < 1) {
  8339. return $this->raiseError("Please supply the package you want to bundle");
  8340. }
  8341. if (isset($options['destination'])) {
  8342. if (!is_dir($options['destination'])) {
  8343. System::mkdir('-p ' . $options['destination']);
  8344. }
  8345. $dest = realpath($options['destination']);
  8346. } else {
  8347. $pwd = getcwd();
  8348. $dir = $pwd . DIRECTORY_SEPARATOR . 'ext';
  8349. $dest = is_dir($dir) ? $dir : $pwd;
  8350. }
  8351. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  8352. $err = $downloader->setDownloadDir($dest);
  8353. PEAR::staticPopErrorHandling();
  8354. if (PEAR::isError($err)) {
  8355. return PEAR::raiseError('download directory "' . $dest .
  8356. '" is not writeable.');
  8357. }
  8358. $result = &$downloader->download(array($params[0]));
  8359. if (PEAR::isError($result)) {
  8360. return $result;
  8361. }
  8362. if (!isset($result[0])) {
  8363. return $this->raiseError('unable to unpack ' . $params[0]);
  8364. }
  8365. $pkgfile = &$result[0]->getPackageFile();
  8366. $pkgname = $pkgfile->getName();
  8367. $pkgversion = $pkgfile->getVersion();
  8368. // Unpacking -------------------------------------------------
  8369. $dest .= DIRECTORY_SEPARATOR . $pkgname;
  8370. $orig = $pkgname . '-' . $pkgversion;
  8371. $tar = new Archive_Tar($pkgfile->getArchiveFile());
  8372. if (!$tar->extractModify($dest, $orig)) {
  8373. return $this->raiseError('unable to unpack ' . $pkgfile->getArchiveFile());
  8374. }
  8375. $this->ui->outputData("Package ready at '$dest'");
  8376. // }}}
  8377. }
  8378. // }}}
  8379. function doRunScripts($command, $options, $params)
  8380. {
  8381. if (!isset($params[0])) {
  8382. return $this->raiseError('run-scripts expects 1 parameter: a package name');
  8383. }
  8384. $reg = &$this->config->getRegistry();
  8385. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  8386. $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
  8387. PEAR::staticPopErrorHandling();
  8388. if (PEAR::isError($parsed)) {
  8389. return $this->raiseError($parsed);
  8390. }
  8391. $package = &$reg->getPackage($parsed['package'], $parsed['channel']);
  8392. if (!is_object($package)) {
  8393. return $this->raiseError('Could not retrieve package "' . $params[0] . '" from registry');
  8394. }
  8395. $package->setConfig($this->config);
  8396. $package->runPostinstallScripts();
  8397. $this->ui->outputData('Install scripts complete', $command);
  8398. return true;
  8399. }
  8400. /**
  8401. * Given a list of packages, filter out those ones that are already up to date
  8402. *
  8403. * @param $packages: packages, in parsed array format !
  8404. * @return list of packages that can be upgraded
  8405. */
  8406. function _filterUptodatePackages($packages, $command)
  8407. {
  8408. $reg = &$this->config->getRegistry();
  8409. $latestReleases = array();
  8410. $ret = array();
  8411. foreach ($packages as $package) {
  8412. if (isset($package['group'])) {
  8413. $ret[] = $package;
  8414. continue;
  8415. }
  8416. $channel = $package['channel'];
  8417. $name = $package['package'];
  8418. if (!$reg->packageExists($name, $channel)) {
  8419. $ret[] = $package;
  8420. continue;
  8421. }
  8422. if (!isset($latestReleases[$channel])) {
  8423. // fill in cache for this channel
  8424. $chan = $reg->getChannel($channel);
  8425. if (PEAR::isError($chan)) {
  8426. return $this->raiseError($chan);
  8427. }
  8428. $base2 = false;
  8429. $preferred_mirror = $this->config->get('preferred_mirror', null, $channel);
  8430. if ($chan->supportsREST($preferred_mirror) &&
  8431. (
  8432. //($base2 = $chan->getBaseURL('REST1.4', $preferred_mirror)) ||
  8433. ($base = $chan->getBaseURL('REST1.0', $preferred_mirror))
  8434. )
  8435. ) {
  8436. $dorest = true;
  8437. }
  8438. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  8439. if (!isset($package['state'])) {
  8440. $state = $this->config->get('preferred_state', null, $channel);
  8441. } else {
  8442. $state = $package['state'];
  8443. }
  8444. if ($dorest) {
  8445. if ($base2) {
  8446. $rest = &$this->config->getREST('1.4', array());
  8447. $base = $base2;
  8448. } else {
  8449. $rest = &$this->config->getREST('1.0', array());
  8450. }
  8451. $installed = array_flip($reg->listPackages($channel));
  8452. $latest = $rest->listLatestUpgrades($base, $state, $installed, $channel, $reg);
  8453. }
  8454. PEAR::staticPopErrorHandling();
  8455. if (PEAR::isError($latest)) {
  8456. $this->ui->outputData('Error getting channel info from ' . $channel .
  8457. ': ' . $latest->getMessage());
  8458. continue;
  8459. }
  8460. $latestReleases[$channel] = array_change_key_case($latest);
  8461. }
  8462. // check package for latest release
  8463. $name_lower = strtolower($name);
  8464. if (isset($latestReleases[$channel][$name_lower])) {
  8465. // if not set, up to date
  8466. $inst_version = $reg->packageInfo($name, 'version', $channel);
  8467. $channel_version = $latestReleases[$channel][$name_lower]['version'];
  8468. if (version_compare($channel_version, $inst_version, 'le')) {
  8469. // installed version is up-to-date
  8470. continue;
  8471. }
  8472. // maintain BC
  8473. if ($command == 'upgrade-all') {
  8474. $this->ui->outputData(array('data' => 'Will upgrade ' .
  8475. $reg->parsedPackageNameToString($package)), $command);
  8476. }
  8477. $ret[] = $package;
  8478. }
  8479. }
  8480. return $ret;
  8481. }
  8482. }
  8483. <commands version="1.0">
  8484. <install>
  8485. <summary>Install Package</summary>
  8486. <function>doInstall</function>
  8487. <shortcut>i</shortcut>
  8488. <options>
  8489. <force>
  8490. <shortopt>f</shortopt>
  8491. <doc>will overwrite newer installed packages</doc>
  8492. </force>
  8493. <loose>
  8494. <shortopt>l</shortopt>
  8495. <doc>do not check for recommended dependency version</doc>
  8496. </loose>
  8497. <nodeps>
  8498. <shortopt>n</shortopt>
  8499. <doc>ignore dependencies, install anyway</doc>
  8500. </nodeps>
  8501. <register-only>
  8502. <shortopt>r</shortopt>
  8503. <doc>do not install files, only register the package as installed</doc>
  8504. </register-only>
  8505. <soft>
  8506. <shortopt>s</shortopt>
  8507. <doc>soft install, fail silently, or upgrade if already installed</doc>
  8508. </soft>
  8509. <nobuild>
  8510. <shortopt>B</shortopt>
  8511. <doc>don&#039;t build C extensions</doc>
  8512. </nobuild>
  8513. <configureoptions>
  8514. <shortopt>D</shortopt>
  8515. <arg>OPTION1=VALUE[ OPTION2=VALUE]</arg>
  8516. </configureoptions>
  8517. <nocompress>
  8518. <shortopt>Z</shortopt>
  8519. <doc>request uncompressed files when downloading</doc>
  8520. </nocompress>
  8521. <installroot>
  8522. <shortopt>R</shortopt>
  8523. <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT), use packagingroot for RPM</doc>
  8524. <arg>DIR</arg>
  8525. </installroot>
  8526. <packagingroot>
  8527. <shortopt>P</shortopt>
  8528. <doc>root directory used when packaging files, like RPM packaging</doc>
  8529. <arg>DIR</arg>
  8530. </packagingroot>
  8531. <ignore-errors>
  8532. <shortopt></shortopt>
  8533. <doc>force install even if there were errors</doc>
  8534. </ignore-errors>
  8535. <alldeps>
  8536. <shortopt>a</shortopt>
  8537. <doc>install all required and optional dependencies</doc>
  8538. </alldeps>
  8539. <onlyreqdeps>
  8540. <shortopt>o</shortopt>
  8541. <doc>install all required dependencies</doc>
  8542. </onlyreqdeps>
  8543. <offline>
  8544. <shortopt>O</shortopt>
  8545. <doc>do not attempt to download any urls or contact channels</doc>
  8546. </offline>
  8547. <pretend>
  8548. <shortopt>p</shortopt>
  8549. <doc>Only list the packages that would be downloaded</doc>
  8550. </pretend>
  8551. </options>
  8552. <doc>[channel/]&lt;package&gt; ...
  8553. Installs one or more PEAR packages. You can specify a package to
  8554. install in four ways:
  8555. &quot;Package-1.0.tgz&quot; : installs from a local file
  8556. &quot;http://example.com/Package-1.0.tgz&quot; : installs from
  8557. anywhere on the net.
  8558. &quot;package.xml&quot; : installs the package described in
  8559. package.xml. Useful for testing, or for wrapping a PEAR package in
  8560. another package manager such as RPM.
  8561. &quot;Package[-version/state][.tar]&quot; : queries your default channel&#039;s server
  8562. ({config master_server}) and downloads the newest package with
  8563. the preferred quality/state ({config preferred_state}).
  8564. To retrieve Package version 1.1, use &quot;Package-1.1,&quot; to retrieve
  8565. Package state beta, use &quot;Package-beta.&quot; To retrieve an uncompressed
  8566. file, append .tar (make sure there is no file by the same name first)
  8567. To download a package from another channel, prefix with the channel name like
  8568. &quot;channel/Package&quot;
  8569. More than one package may be specified at once. It is ok to mix these
  8570. four ways of specifying packages.
  8571. </doc>
  8572. </install>
  8573. <upgrade>
  8574. <summary>Upgrade Package</summary>
  8575. <function>doInstall</function>
  8576. <shortcut>up</shortcut>
  8577. <options>
  8578. <channel>
  8579. <shortopt>c</shortopt>
  8580. <doc>upgrade packages from a specific channel</doc>
  8581. <arg>CHAN</arg>
  8582. </channel>
  8583. <force>
  8584. <shortopt>f</shortopt>
  8585. <doc>overwrite newer installed packages</doc>
  8586. </force>
  8587. <loose>
  8588. <shortopt>l</shortopt>
  8589. <doc>do not check for recommended dependency version</doc>
  8590. </loose>
  8591. <nodeps>
  8592. <shortopt>n</shortopt>
  8593. <doc>ignore dependencies, upgrade anyway</doc>
  8594. </nodeps>
  8595. <register-only>
  8596. <shortopt>r</shortopt>
  8597. <doc>do not install files, only register the package as upgraded</doc>
  8598. </register-only>
  8599. <nobuild>
  8600. <shortopt>B</shortopt>
  8601. <doc>don&#039;t build C extensions</doc>
  8602. </nobuild>
  8603. <nocompress>
  8604. <shortopt>Z</shortopt>
  8605. <doc>request uncompressed files when downloading</doc>
  8606. </nocompress>
  8607. <installroot>
  8608. <shortopt>R</shortopt>
  8609. <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT)</doc>
  8610. <arg>DIR</arg>
  8611. </installroot>
  8612. <ignore-errors>
  8613. <shortopt></shortopt>
  8614. <doc>force install even if there were errors</doc>
  8615. </ignore-errors>
  8616. <alldeps>
  8617. <shortopt>a</shortopt>
  8618. <doc>install all required and optional dependencies</doc>
  8619. </alldeps>
  8620. <onlyreqdeps>
  8621. <shortopt>o</shortopt>
  8622. <doc>install all required dependencies</doc>
  8623. </onlyreqdeps>
  8624. <offline>
  8625. <shortopt>O</shortopt>
  8626. <doc>do not attempt to download any urls or contact channels</doc>
  8627. </offline>
  8628. <pretend>
  8629. <shortopt>p</shortopt>
  8630. <doc>Only list the packages that would be downloaded</doc>
  8631. </pretend>
  8632. </options>
  8633. <doc>&lt;package&gt; ...
  8634. Upgrades one or more PEAR packages. See documentation for the
  8635. &quot;install&quot; command for ways to specify a package.
  8636. When upgrading, your package will be updated if the provided new
  8637. package has a higher version number (use the -f option if you need to
  8638. upgrade anyway).
  8639. More than one package may be specified at once.
  8640. </doc>
  8641. </upgrade>
  8642. <upgrade-all>
  8643. <summary>Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters]</summary>
  8644. <function>doUpgradeAll</function>
  8645. <shortcut>ua</shortcut>
  8646. <options>
  8647. <channel>
  8648. <shortopt>c</shortopt>
  8649. <doc>upgrade packages from a specific channel</doc>
  8650. <arg>CHAN</arg>
  8651. </channel>
  8652. <nodeps>
  8653. <shortopt>n</shortopt>
  8654. <doc>ignore dependencies, upgrade anyway</doc>
  8655. </nodeps>
  8656. <register-only>
  8657. <shortopt>r</shortopt>
  8658. <doc>do not install files, only register the package as upgraded</doc>
  8659. </register-only>
  8660. <nobuild>
  8661. <shortopt>B</shortopt>
  8662. <doc>don&#039;t build C extensions</doc>
  8663. </nobuild>
  8664. <nocompress>
  8665. <shortopt>Z</shortopt>
  8666. <doc>request uncompressed files when downloading</doc>
  8667. </nocompress>
  8668. <installroot>
  8669. <shortopt>R</shortopt>
  8670. <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT), use packagingroot for RPM</doc>
  8671. <arg>DIR</arg>
  8672. </installroot>
  8673. <ignore-errors>
  8674. <shortopt></shortopt>
  8675. <doc>force install even if there were errors</doc>
  8676. </ignore-errors>
  8677. <loose>
  8678. <shortopt></shortopt>
  8679. <doc>do not check for recommended dependency version</doc>
  8680. </loose>
  8681. </options>
  8682. <doc>
  8683. WARNING: This function is deprecated in favor of using the upgrade command with no params
  8684. Upgrades all packages that have a newer release available. Upgrades are
  8685. done only if there is a release available of the state specified in
  8686. &quot;preferred_state&quot; (currently {config preferred_state}), or a state considered
  8687. more stable.
  8688. </doc>
  8689. </upgrade-all>
  8690. <uninstall>
  8691. <summary>Un-install Package</summary>
  8692. <function>doUninstall</function>
  8693. <shortcut>un</shortcut>
  8694. <options>
  8695. <nodeps>
  8696. <shortopt>n</shortopt>
  8697. <doc>ignore dependencies, uninstall anyway</doc>
  8698. </nodeps>
  8699. <register-only>
  8700. <shortopt>r</shortopt>
  8701. <doc>do not remove files, only register the packages as not installed</doc>
  8702. </register-only>
  8703. <installroot>
  8704. <shortopt>R</shortopt>
  8705. <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT)</doc>
  8706. <arg>DIR</arg>
  8707. </installroot>
  8708. <ignore-errors>
  8709. <shortopt></shortopt>
  8710. <doc>force install even if there were errors</doc>
  8711. </ignore-errors>
  8712. <offline>
  8713. <shortopt>O</shortopt>
  8714. <doc>do not attempt to uninstall remotely</doc>
  8715. </offline>
  8716. </options>
  8717. <doc>[channel/]&lt;package&gt; ...
  8718. Uninstalls one or more PEAR packages. More than one package may be
  8719. specified at once. Prefix with channel name to uninstall from a
  8720. channel not in your default channel ({config default_channel})
  8721. </doc>
  8722. </uninstall>
  8723. <bundle>
  8724. <summary>Unpacks a Pecl Package</summary>
  8725. <function>doBundle</function>
  8726. <shortcut>bun</shortcut>
  8727. <options>
  8728. <destination>
  8729. <shortopt>d</shortopt>
  8730. <doc>Optional destination directory for unpacking (defaults to current path or &quot;ext&quot; if exists)</doc>
  8731. <arg>DIR</arg>
  8732. </destination>
  8733. <force>
  8734. <shortopt>f</shortopt>
  8735. <doc>Force the unpacking even if there were errors in the package</doc>
  8736. </force>
  8737. </options>
  8738. <doc>&lt;package&gt;
  8739. Unpacks a Pecl Package into the selected location. It will download the
  8740. package if needed.
  8741. </doc>
  8742. </bundle>
  8743. <run-scripts>
  8744. <summary>Run Post-Install Scripts bundled with a package</summary>
  8745. <function>doRunScripts</function>
  8746. <shortcut>rs</shortcut>
  8747. <options />
  8748. <doc>&lt;package&gt;
  8749. Run post-installation scripts in package &lt;package&gt;, if any exist.
  8750. </doc>
  8751. </run-scripts>
  8752. </commands><?php
  8753. /**
  8754. * PEAR_Common, the base class for the PEAR Installer
  8755. *
  8756. * PHP versions 4 and 5
  8757. *
  8758. * @category pear
  8759. * @package PEAR
  8760. * @author Stig Bakken <ssb@php.net>
  8761. * @author Tomas V. V. Cox <cox@idecnet.com>
  8762. * @author Greg Beaver <cellog@php.net>
  8763. * @copyright 1997-2009 The Authors
  8764. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  8765. * @link http://pear.php.net/package/PEAR
  8766. * @since File available since Release 0.1.0
  8767. * @deprecated File deprecated since Release 1.4.0a1
  8768. */
  8769. /**
  8770. * Include error handling
  8771. */
  8772. require_once 'phar://go-pear.phar/' . 'PEAR.php';
  8773. /**
  8774. * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode()
  8775. */
  8776. define('PEAR_COMMON_ERROR_INVALIDPHP', 1);
  8777. define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+');
  8778. define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/');
  8779. // this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1
  8780. define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?');
  8781. define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '\\z/i');
  8782. // XXX far from perfect :-)
  8783. define('_PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '(' . _PEAR_COMMON_PACKAGE_NAME_PREG .
  8784. ')(-([.0-9a-zA-Z]+))?');
  8785. define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_PACKAGE_DOWNLOAD_PREG .
  8786. '\\z/');
  8787. define('_PEAR_CHANNELS_NAME_PREG', '[A-Za-z][a-zA-Z0-9\.]+');
  8788. define('PEAR_CHANNELS_NAME_PREG', '/^' . _PEAR_CHANNELS_NAME_PREG . '\\z/');
  8789. // this should allow any dns or IP address, plus a path - NO UNDERSCORES ALLOWED
  8790. define('_PEAR_CHANNELS_SERVER_PREG', '[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*(\/[a-zA-Z0-9\-]+)*');
  8791. define('PEAR_CHANNELS_SERVER_PREG', '/^' . _PEAR_CHANNELS_SERVER_PREG . '\\z/i');
  8792. define('_PEAR_CHANNELS_PACKAGE_PREG', '(' ._PEAR_CHANNELS_SERVER_PREG . ')\/('
  8793. . _PEAR_COMMON_PACKAGE_NAME_PREG . ')');
  8794. define('PEAR_CHANNELS_PACKAGE_PREG', '/^' . _PEAR_CHANNELS_PACKAGE_PREG . '\\z/i');
  8795. define('_PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '(' . _PEAR_CHANNELS_NAME_PREG . ')::('
  8796. . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?');
  8797. define('PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_CHANNEL_DOWNLOAD_PREG . '\\z/');
  8798. /**
  8799. * List of temporary files and directories registered by
  8800. * PEAR_Common::addTempFile().
  8801. * @var array
  8802. */
  8803. $GLOBALS['_PEAR_Common_tempfiles'] = array();
  8804. /**
  8805. * Valid maintainer roles
  8806. * @var array
  8807. */
  8808. $GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper');
  8809. /**
  8810. * Valid release states
  8811. * @var array
  8812. */
  8813. $GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel');
  8814. /**
  8815. * Valid dependency types
  8816. * @var array
  8817. */
  8818. $GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi');
  8819. /**
  8820. * Valid dependency relations
  8821. * @var array
  8822. */
  8823. $GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not', 'ne');
  8824. /**
  8825. * Valid file roles
  8826. * @var array
  8827. */
  8828. $GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script');
  8829. /**
  8830. * Valid replacement types
  8831. * @var array
  8832. */
  8833. $GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info');
  8834. /**
  8835. * Valid "provide" types
  8836. * @var array
  8837. */
  8838. $GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api');
  8839. /**
  8840. * Valid "provide" types
  8841. * @var array
  8842. */
  8843. $GLOBALS['_PEAR_Common_script_phases'] = array('pre-install', 'post-install', 'pre-uninstall', 'post-uninstall', 'pre-build', 'post-build', 'pre-configure', 'post-configure', 'pre-setup', 'post-setup');
  8844. /**
  8845. * Class providing common functionality for PEAR administration classes.
  8846. * @category pear
  8847. * @package PEAR
  8848. * @author Stig Bakken <ssb@php.net>
  8849. * @author Tomas V. V. Cox <cox@idecnet.com>
  8850. * @author Greg Beaver <cellog@php.net>
  8851. * @copyright 1997-2009 The Authors
  8852. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  8853. * @version Release: 1.10.16
  8854. * @link http://pear.php.net/package/PEAR
  8855. * @since Class available since Release 1.4.0a1
  8856. * @deprecated This class will disappear, and its components will be spread
  8857. * into smaller classes, like the AT&T breakup, as of Release 1.4.0a1
  8858. */
  8859. class PEAR_Common extends PEAR
  8860. {
  8861. /**
  8862. * User Interface object (PEAR_Frontend_* class). If null,
  8863. * the log() method uses print.
  8864. * @var object
  8865. */
  8866. var $ui = null;
  8867. /**
  8868. * Configuration object (PEAR_Config).
  8869. * @var PEAR_Config
  8870. */
  8871. var $config = null;
  8872. /** stack of elements, gives some sort of XML context */
  8873. var $element_stack = array();
  8874. /** name of currently parsed XML element */
  8875. var $current_element;
  8876. /** array of attributes of the currently parsed XML element */
  8877. var $current_attributes = array();
  8878. /** assoc with information about a package */
  8879. var $pkginfo = array();
  8880. var $current_path = null;
  8881. /**
  8882. * Flag variable used to mark a valid package file
  8883. * @var boolean
  8884. * @access private
  8885. */
  8886. var $_validPackageFile;
  8887. /**
  8888. * PEAR_Common constructor
  8889. *
  8890. * @access public
  8891. */
  8892. function __construct()
  8893. {
  8894. parent::__construct();
  8895. $this->config = &PEAR_Config::singleton();
  8896. $this->debug = $this->config->get('verbose');
  8897. }
  8898. /**
  8899. * PEAR_Common destructor
  8900. *
  8901. * @access private
  8902. */
  8903. function _PEAR_Common()
  8904. {
  8905. // doesn't work due to bug #14744
  8906. //$tempfiles = $this->_tempfiles;
  8907. $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles'];
  8908. while ($file = array_shift($tempfiles)) {
  8909. if (@is_dir($file)) {
  8910. if (!class_exists('System')) {
  8911. require_once 'phar://go-pear.phar/' . 'System.php';
  8912. }
  8913. System::rm(array('-rf', $file));
  8914. } elseif (file_exists($file)) {
  8915. unlink($file);
  8916. }
  8917. }
  8918. }
  8919. /**
  8920. * Register a temporary file or directory. When the destructor is
  8921. * executed, all registered temporary files and directories are
  8922. * removed.
  8923. *
  8924. * @param string $file name of file or directory
  8925. *
  8926. * @return void
  8927. *
  8928. * @access public
  8929. */
  8930. static function addTempFile($file)
  8931. {
  8932. if (!class_exists('PEAR_Frontend')) {
  8933. require_once 'phar://go-pear.phar/' . 'PEAR/Frontend.php';
  8934. }
  8935. PEAR_Frontend::addTempFile($file);
  8936. }
  8937. /**
  8938. * Wrapper to System::mkDir(), creates a directory as well as
  8939. * any necessary parent directories.
  8940. *
  8941. * @param string $dir directory name
  8942. *
  8943. * @return bool TRUE on success, or a PEAR error
  8944. *
  8945. * @access public
  8946. */
  8947. function mkDirHier($dir)
  8948. {
  8949. // Only used in Installer - move it there ?
  8950. $this->log(2, "+ create dir $dir");
  8951. if (!class_exists('System')) {
  8952. require_once 'phar://go-pear.phar/' . 'System.php';
  8953. }
  8954. return System::mkDir(array('-p', $dir));
  8955. }
  8956. /**
  8957. * Logging method.
  8958. *
  8959. * @param int $level log level (0 is quiet, higher is noisier)
  8960. * @param string $msg message to write to the log
  8961. *
  8962. * @return void
  8963. */
  8964. public function log($level, $msg, $append_crlf = true)
  8965. {
  8966. if ($this->debug >= $level) {
  8967. if (!class_exists('PEAR_Frontend')) {
  8968. require_once 'phar://go-pear.phar/' . 'PEAR/Frontend.php';
  8969. }
  8970. $ui = &PEAR_Frontend::singleton();
  8971. if (is_a($ui, 'PEAR_Frontend')) {
  8972. $ui->log($msg, $append_crlf);
  8973. } else {
  8974. print "$msg\n";
  8975. }
  8976. }
  8977. }
  8978. /**
  8979. * Create and register a temporary directory.
  8980. *
  8981. * @param string $tmpdir (optional) Directory to use as tmpdir.
  8982. * Will use system defaults (for example
  8983. * /tmp or c:\windows\temp) if not specified
  8984. *
  8985. * @return string name of created directory
  8986. *
  8987. * @access public
  8988. */
  8989. function mkTempDir($tmpdir = '')
  8990. {
  8991. $topt = $tmpdir ? array('-t', $tmpdir) : array();
  8992. $topt = array_merge($topt, array('-d', 'pear'));
  8993. if (!class_exists('System')) {
  8994. require_once 'phar://go-pear.phar/' . 'System.php';
  8995. }
  8996. if (!$tmpdir = System::mktemp($topt)) {
  8997. return false;
  8998. }
  8999. self::addTempFile($tmpdir);
  9000. return $tmpdir;
  9001. }
  9002. /**
  9003. * Set object that represents the frontend to be used.
  9004. *
  9005. * @param object Reference of the frontend object
  9006. * @return void
  9007. * @access public
  9008. */
  9009. function setFrontendObject(&$ui)
  9010. {
  9011. $this->ui = &$ui;
  9012. }
  9013. /**
  9014. * Return an array containing all of the states that are more stable than
  9015. * or equal to the passed in state
  9016. *
  9017. * @param string Release state
  9018. * @param boolean Determines whether to include $state in the list
  9019. * @return false|array False if $state is not a valid release state
  9020. */
  9021. static function betterStates($state, $include = false)
  9022. {
  9023. static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
  9024. $i = array_search($state, $states);
  9025. if ($i === false) {
  9026. return false;
  9027. }
  9028. if ($include) {
  9029. $i--;
  9030. }
  9031. return array_slice($states, $i + 1);
  9032. }
  9033. /**
  9034. * Get the valid roles for a PEAR package maintainer
  9035. *
  9036. * @return array
  9037. */
  9038. public static function getUserRoles()
  9039. {
  9040. return $GLOBALS['_PEAR_Common_maintainer_roles'];
  9041. }
  9042. /**
  9043. * Get the valid package release states of packages
  9044. *
  9045. * @return array
  9046. */
  9047. public static function getReleaseStates()
  9048. {
  9049. return $GLOBALS['_PEAR_Common_release_states'];
  9050. }
  9051. /**
  9052. * Get the implemented dependency types (php, ext, pkg etc.)
  9053. *
  9054. * @return array
  9055. */
  9056. public static function getDependencyTypes()
  9057. {
  9058. return $GLOBALS['_PEAR_Common_dependency_types'];
  9059. }
  9060. /**
  9061. * Get the implemented dependency relations (has, lt, ge etc.)
  9062. *
  9063. * @return array
  9064. */
  9065. public static function getDependencyRelations()
  9066. {
  9067. return $GLOBALS['_PEAR_Common_dependency_relations'];
  9068. }
  9069. /**
  9070. * Get the implemented file roles
  9071. *
  9072. * @return array
  9073. */
  9074. public static function getFileRoles()
  9075. {
  9076. return $GLOBALS['_PEAR_Common_file_roles'];
  9077. }
  9078. /**
  9079. * Get the implemented file replacement types in
  9080. *
  9081. * @return array
  9082. */
  9083. public static function getReplacementTypes()
  9084. {
  9085. return $GLOBALS['_PEAR_Common_replacement_types'];
  9086. }
  9087. /**
  9088. * Get the implemented file replacement types in
  9089. *
  9090. * @return array
  9091. */
  9092. public static function getProvideTypes()
  9093. {
  9094. return $GLOBALS['_PEAR_Common_provide_types'];
  9095. }
  9096. /**
  9097. * Get the implemented file replacement types in
  9098. *
  9099. * @return array
  9100. */
  9101. public static function getScriptPhases()
  9102. {
  9103. return $GLOBALS['_PEAR_Common_script_phases'];
  9104. }
  9105. /**
  9106. * Test whether a string contains a valid package name.
  9107. *
  9108. * @param string $name the package name to test
  9109. *
  9110. * @return bool
  9111. *
  9112. * @access public
  9113. */
  9114. function validPackageName($name)
  9115. {
  9116. return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name);
  9117. }
  9118. /**
  9119. * Test whether a string contains a valid package version.
  9120. *
  9121. * @param string $ver the package version to test
  9122. *
  9123. * @return bool
  9124. *
  9125. * @access public
  9126. */
  9127. function validPackageVersion($ver)
  9128. {
  9129. return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
  9130. }
  9131. /**
  9132. * @param string $path relative or absolute include path
  9133. * @return boolean
  9134. */
  9135. public static function isIncludeable($path)
  9136. {
  9137. if (file_exists($path) && is_readable($path)) {
  9138. return true;
  9139. }
  9140. $ipath = explode(PATH_SEPARATOR, ini_get('include_path'));
  9141. foreach ($ipath as $include) {
  9142. $test = realpath($include . DIRECTORY_SEPARATOR . $path);
  9143. if (file_exists($test) && is_readable($test)) {
  9144. return true;
  9145. }
  9146. }
  9147. return false;
  9148. }
  9149. function _postProcessChecks($pf)
  9150. {
  9151. if (!PEAR::isError($pf)) {
  9152. return $this->_postProcessValidPackagexml($pf);
  9153. }
  9154. $errs = $pf->getUserinfo();
  9155. if (is_array($errs)) {
  9156. foreach ($errs as $error) {
  9157. $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
  9158. }
  9159. }
  9160. return $pf;
  9161. }
  9162. /**
  9163. * Returns information about a package file. Expects the name of
  9164. * a gzipped tar file as input.
  9165. *
  9166. * @param string $file name of .tgz file
  9167. *
  9168. * @return array array with package information
  9169. *
  9170. * @access public
  9171. * @deprecated use PEAR_PackageFile->fromTgzFile() instead
  9172. *
  9173. */
  9174. function infoFromTgzFile($file)
  9175. {
  9176. $packagefile = new PEAR_PackageFile($this->config);
  9177. $pf = &$packagefile->fromTgzFile($file, PEAR_VALIDATE_NORMAL);
  9178. return $this->_postProcessChecks($pf);
  9179. }
  9180. /**
  9181. * Returns information about a package file. Expects the name of
  9182. * a package xml file as input.
  9183. *
  9184. * @param string $descfile name of package xml file
  9185. *
  9186. * @return array array with package information
  9187. *
  9188. * @access public
  9189. * @deprecated use PEAR_PackageFile->fromPackageFile() instead
  9190. *
  9191. */
  9192. function infoFromDescriptionFile($descfile)
  9193. {
  9194. $packagefile = new PEAR_PackageFile($this->config);
  9195. $pf = &$packagefile->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
  9196. return $this->_postProcessChecks($pf);
  9197. }
  9198. /**
  9199. * Returns information about a package file. Expects the contents
  9200. * of a package xml file as input.
  9201. *
  9202. * @param string $data contents of package.xml file
  9203. *
  9204. * @return array array with package information
  9205. *
  9206. * @access public
  9207. * @deprecated use PEAR_PackageFile->fromXmlstring() instead
  9208. *
  9209. */
  9210. function infoFromString($data)
  9211. {
  9212. $packagefile = new PEAR_PackageFile($this->config);
  9213. $pf = &$packagefile->fromXmlString($data, PEAR_VALIDATE_NORMAL, false);
  9214. return $this->_postProcessChecks($pf);
  9215. }
  9216. /**
  9217. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  9218. * @return array
  9219. */
  9220. function _postProcessValidPackagexml(&$pf)
  9221. {
  9222. if (!is_a($pf, 'PEAR_PackageFile_v2')) {
  9223. $this->pkginfo = $pf->toArray();
  9224. return $this->pkginfo;
  9225. }
  9226. // sort of make this into a package.xml 1.0-style array
  9227. // changelog is not converted to old format.
  9228. $arr = $pf->toArray(true);
  9229. $arr = array_merge($arr, $arr['old']);
  9230. unset($arr['old'], $arr['xsdversion'], $arr['contents'], $arr['compatible'],
  9231. $arr['channel'], $arr['uri'], $arr['dependencies'], $arr['phprelease'],
  9232. $arr['extsrcrelease'], $arr['zendextsrcrelease'], $arr['extbinrelease'],
  9233. $arr['zendextbinrelease'], $arr['bundle'], $arr['lead'], $arr['developer'],
  9234. $arr['helper'], $arr['contributor']);
  9235. $arr['filelist'] = $pf->getFilelist();
  9236. $this->pkginfo = $arr;
  9237. return $arr;
  9238. }
  9239. /**
  9240. * Returns package information from different sources
  9241. *
  9242. * This method is able to extract information about a package
  9243. * from a .tgz archive or from a XML package definition file.
  9244. *
  9245. * @access public
  9246. * @param string Filename of the source ('package.xml', '<package>.tgz')
  9247. * @return string
  9248. * @deprecated use PEAR_PackageFile->fromAnyFile() instead
  9249. */
  9250. function infoFromAny($info)
  9251. {
  9252. if (is_string($info) && file_exists($info)) {
  9253. $packagefile = new PEAR_PackageFile($this->config);
  9254. $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
  9255. if (PEAR::isError($pf)) {
  9256. $errs = $pf->getUserinfo();
  9257. if (is_array($errs)) {
  9258. foreach ($errs as $error) {
  9259. $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
  9260. }
  9261. }
  9262. return $pf;
  9263. }
  9264. return $this->_postProcessValidPackagexml($pf);
  9265. }
  9266. return $info;
  9267. }
  9268. /**
  9269. * Return an XML document based on the package info (as returned
  9270. * by the PEAR_Common::infoFrom* methods).
  9271. *
  9272. * @param array $pkginfo package info
  9273. *
  9274. * @return string XML data
  9275. *
  9276. * @access public
  9277. * @deprecated use a PEAR_PackageFile_v* object's generator instead
  9278. */
  9279. function xmlFromInfo($pkginfo)
  9280. {
  9281. $config = &PEAR_Config::singleton();
  9282. $packagefile = new PEAR_PackageFile($config);
  9283. $pf = &$packagefile->fromArray($pkginfo);
  9284. $gen = &$pf->getDefaultGenerator();
  9285. return $gen->toXml(PEAR_VALIDATE_PACKAGING);
  9286. }
  9287. /**
  9288. * Validate XML package definition file.
  9289. *
  9290. * @param string $info Filename of the package archive or of the
  9291. * package definition file
  9292. * @param array $errors Array that will contain the errors
  9293. * @param array $warnings Array that will contain the warnings
  9294. * @param string $dir_prefix (optional) directory where source files
  9295. * may be found, or empty if they are not available
  9296. * @access public
  9297. * @return boolean
  9298. * @deprecated use the validation of PEAR_PackageFile objects
  9299. */
  9300. function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '')
  9301. {
  9302. $config = &PEAR_Config::singleton();
  9303. $packagefile = new PEAR_PackageFile($config);
  9304. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  9305. if (strpos($info, '<?xml') !== false) {
  9306. $pf = &$packagefile->fromXmlString($info, PEAR_VALIDATE_NORMAL, '');
  9307. } else {
  9308. $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
  9309. }
  9310. PEAR::staticPopErrorHandling();
  9311. if (PEAR::isError($pf)) {
  9312. $errs = $pf->getUserinfo();
  9313. if (is_array($errs)) {
  9314. foreach ($errs as $error) {
  9315. if ($error['level'] == 'error') {
  9316. $errors[] = $error['message'];
  9317. } else {
  9318. $warnings[] = $error['message'];
  9319. }
  9320. }
  9321. }
  9322. return false;
  9323. }
  9324. return true;
  9325. }
  9326. /**
  9327. * Build a "provides" array from data returned by
  9328. * analyzeSourceCode(). The format of the built array is like
  9329. * this:
  9330. *
  9331. * array(
  9332. * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
  9333. * ...
  9334. * )
  9335. *
  9336. *
  9337. * @param array $srcinfo array with information about a source file
  9338. * as returned by the analyzeSourceCode() method.
  9339. *
  9340. * @return void
  9341. *
  9342. * @access public
  9343. *
  9344. */
  9345. function buildProvidesArray($srcinfo)
  9346. {
  9347. $file = basename($srcinfo['source_file']);
  9348. $pn = '';
  9349. if (isset($this->_packageName)) {
  9350. $pn = $this->_packageName;
  9351. }
  9352. $pnl = strlen($pn);
  9353. foreach ($srcinfo['declared_classes'] as $class) {
  9354. $key = "class;$class";
  9355. if (isset($this->pkginfo['provides'][$key])) {
  9356. continue;
  9357. }
  9358. $this->pkginfo['provides'][$key] =
  9359. array('file'=> $file, 'type' => 'class', 'name' => $class);
  9360. if (isset($srcinfo['inheritance'][$class])) {
  9361. $this->pkginfo['provides'][$key]['extends'] =
  9362. $srcinfo['inheritance'][$class];
  9363. }
  9364. }
  9365. foreach ($srcinfo['declared_methods'] as $class => $methods) {
  9366. foreach ($methods as $method) {
  9367. $function = "$class::$method";
  9368. $key = "function;$function";
  9369. if ($method[0] == '_' || !strcasecmp($method, $class) ||
  9370. isset($this->pkginfo['provides'][$key])) {
  9371. continue;
  9372. }
  9373. $this->pkginfo['provides'][$key] =
  9374. array('file'=> $file, 'type' => 'function', 'name' => $function);
  9375. }
  9376. }
  9377. foreach ($srcinfo['declared_functions'] as $function) {
  9378. $key = "function;$function";
  9379. if ($function[0] == '_' || isset($this->pkginfo['provides'][$key])) {
  9380. continue;
  9381. }
  9382. if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
  9383. $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
  9384. }
  9385. $this->pkginfo['provides'][$key] =
  9386. array('file'=> $file, 'type' => 'function', 'name' => $function);
  9387. }
  9388. }
  9389. /**
  9390. * Analyze the source code of the given PHP file
  9391. *
  9392. * @param string Filename of the PHP file
  9393. * @return mixed
  9394. * @access public
  9395. */
  9396. static function analyzeSourceCode($file)
  9397. {
  9398. if (!class_exists('PEAR_PackageFile_v2_Validator')) {
  9399. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/v2/Validator.php';
  9400. }
  9401. $a = new PEAR_PackageFile_v2_Validator;
  9402. return $a->analyzeSourceCode($file);
  9403. }
  9404. function detectDependencies($any, $status_callback = null)
  9405. {
  9406. if (!function_exists("token_get_all")) {
  9407. return false;
  9408. }
  9409. if (PEAR::isError($info = $this->infoFromAny($any))) {
  9410. return $this->raiseError($info);
  9411. }
  9412. if (!is_array($info)) {
  9413. return false;
  9414. }
  9415. $deps = array();
  9416. $used_c = $decl_c = $decl_f = $decl_m = array();
  9417. foreach ($info['filelist'] as $file => $fa) {
  9418. $tmp = $this->analyzeSourceCode($file);
  9419. $used_c = @array_merge($used_c, $tmp['used_classes']);
  9420. $decl_c = @array_merge($decl_c, $tmp['declared_classes']);
  9421. $decl_f = @array_merge($decl_f, $tmp['declared_functions']);
  9422. $decl_m = @array_merge($decl_m, $tmp['declared_methods']);
  9423. $inheri = @array_merge($inheri, $tmp['inheritance']);
  9424. }
  9425. $used_c = array_unique($used_c);
  9426. $decl_c = array_unique($decl_c);
  9427. $undecl_c = array_diff($used_c, $decl_c);
  9428. return array('used_classes' => $used_c,
  9429. 'declared_classes' => $decl_c,
  9430. 'declared_methods' => $decl_m,
  9431. 'declared_functions' => $decl_f,
  9432. 'undeclared_classes' => $undecl_c,
  9433. 'inheritance' => $inheri,
  9434. );
  9435. }
  9436. /**
  9437. * Download a file through HTTP. Considers suggested file name in
  9438. * Content-disposition: header and can run a callback function for
  9439. * different events. The callback will be called with two
  9440. * parameters: the callback type, and parameters. The implemented
  9441. * callback types are:
  9442. *
  9443. * 'setup' called at the very beginning, parameter is a UI object
  9444. * that should be used for all output
  9445. * 'message' the parameter is a string with an informational message
  9446. * 'saveas' may be used to save with a different file name, the
  9447. * parameter is the filename that is about to be used.
  9448. * If a 'saveas' callback returns a non-empty string,
  9449. * that file name will be used as the filename instead.
  9450. * Note that $save_dir will not be affected by this, only
  9451. * the basename of the file.
  9452. * 'start' download is starting, parameter is number of bytes
  9453. * that are expected, or -1 if unknown
  9454. * 'bytesread' parameter is the number of bytes read so far
  9455. * 'done' download is complete, parameter is the total number
  9456. * of bytes read
  9457. * 'connfailed' if the TCP connection fails, this callback is called
  9458. * with array(host,port,errno,errmsg)
  9459. * 'writefailed' if writing to disk fails, this callback is called
  9460. * with array(destfile,errmsg)
  9461. *
  9462. * If an HTTP proxy has been configured (http_proxy PEAR_Config
  9463. * setting), the proxy will be used.
  9464. *
  9465. * @param string $url the URL to download
  9466. * @param object $ui PEAR_Frontend_* instance
  9467. * @param object $config PEAR_Config instance
  9468. * @param string $save_dir (optional) directory to save file in
  9469. * @param mixed $callback (optional) function/method to call for status
  9470. * updates
  9471. * @param false|string|array $lastmodified header values to check against
  9472. * for caching
  9473. * use false to return the header
  9474. * values from this download
  9475. * @param false|array $accept Accept headers to send
  9476. * @param false|string $channel Channel to use for retrieving
  9477. * authentication
  9478. *
  9479. * @return mixed Returns the full path of the downloaded file or a PEAR
  9480. * error on failure. If the error is caused by
  9481. * socket-related errors, the error object will
  9482. * have the fsockopen error code available through
  9483. * getCode(). If caching is requested, then return the header
  9484. * values.
  9485. * If $lastmodified was given and the there are no changes,
  9486. * boolean false is returned.
  9487. *
  9488. * @access public
  9489. */
  9490. function downloadHttp(
  9491. $url, &$ui, $save_dir = '.', $callback = null, $lastmodified = null,
  9492. $accept = false, $channel = false
  9493. ) {
  9494. if (!class_exists('PEAR_Downloader')) {
  9495. require_once 'phar://go-pear.phar/' . 'PEAR/Downloader.php';
  9496. }
  9497. return PEAR_Downloader::_downloadHttp(
  9498. $this, $url, $ui, $save_dir, $callback, $lastmodified,
  9499. $accept, $channel
  9500. );
  9501. }
  9502. }
  9503. require_once 'phar://go-pear.phar/' . 'PEAR/Config.php';
  9504. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile.php';
  9505. <?php
  9506. /**
  9507. * PEAR_Config, customized configuration handling for the PEAR Installer
  9508. *
  9509. * PHP versions 4 and 5
  9510. *
  9511. * @category pear
  9512. * @package PEAR
  9513. * @author Stig Bakken <ssb@php.net>
  9514. * @author Greg Beaver <cellog@php.net>
  9515. * @copyright 1997-2009 The Authors
  9516. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  9517. * @link http://pear.php.net/package/PEAR
  9518. * @since File available since Release 0.1
  9519. */
  9520. /**
  9521. * Required for error handling
  9522. */
  9523. require_once 'phar://go-pear.phar/' . 'PEAR.php';
  9524. require_once 'phar://go-pear.phar/' . 'PEAR/Registry.php';
  9525. require_once 'phar://go-pear.phar/' . 'PEAR/Installer/Role.php';
  9526. require_once 'phar://go-pear.phar/' . 'System.php';
  9527. /**
  9528. * Last created PEAR_Config instance.
  9529. * @var object
  9530. */
  9531. $GLOBALS['_PEAR_Config_instance'] = null;
  9532. if (!defined('PEAR_INSTALL_DIR') || !PEAR_INSTALL_DIR) {
  9533. $PEAR_INSTALL_DIR = PHP_LIBDIR . DIRECTORY_SEPARATOR . 'pear';
  9534. } else {
  9535. $PEAR_INSTALL_DIR = PEAR_INSTALL_DIR;
  9536. }
  9537. // Below we define constants with default values for all configuration
  9538. // parameters except username/password. All of them can have their
  9539. // defaults set through environment variables. The reason we use the
  9540. // PHP_ prefix is for some security, PHP protects environment
  9541. // variables starting with PHP_*.
  9542. // default channel and preferred mirror is based on whether we are invoked through
  9543. // the "pear" or the "pecl" command
  9544. if (!defined('PEAR_RUNTYPE')) {
  9545. define('PEAR_RUNTYPE', 'pear');
  9546. }
  9547. if (PEAR_RUNTYPE == 'pear') {
  9548. define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pear.php.net');
  9549. } else {
  9550. define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pecl.php.net');
  9551. }
  9552. if (getenv('PHP_PEAR_SYSCONF_DIR')) {
  9553. define('PEAR_CONFIG_SYSCONFDIR', getenv('PHP_PEAR_SYSCONF_DIR'));
  9554. } elseif (getenv('SystemRoot')) {
  9555. define('PEAR_CONFIG_SYSCONFDIR', getenv('SystemRoot'));
  9556. } else {
  9557. define('PEAR_CONFIG_SYSCONFDIR', PHP_SYSCONFDIR);
  9558. }
  9559. // Default for master_server
  9560. if (getenv('PHP_PEAR_MASTER_SERVER')) {
  9561. define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', getenv('PHP_PEAR_MASTER_SERVER'));
  9562. } else {
  9563. define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', 'pear.php.net');
  9564. }
  9565. // Default for http_proxy
  9566. if (getenv('PHP_PEAR_HTTP_PROXY')) {
  9567. define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('PHP_PEAR_HTTP_PROXY'));
  9568. } elseif (getenv('http_proxy')) {
  9569. define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('http_proxy'));
  9570. } else {
  9571. define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', '');
  9572. }
  9573. // Default for php_dir
  9574. if (getenv('PHP_PEAR_INSTALL_DIR')) {
  9575. define('PEAR_CONFIG_DEFAULT_PHP_DIR', getenv('PHP_PEAR_INSTALL_DIR'));
  9576. } else {
  9577. define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR);
  9578. }
  9579. // Default for metadata_dir
  9580. if (getenv('PHP_PEAR_METADATA_DIR')) {
  9581. define('PEAR_CONFIG_DEFAULT_METADATA_DIR', getenv('PHP_PEAR_METADATA_DIR'));
  9582. } else {
  9583. define('PEAR_CONFIG_DEFAULT_METADATA_DIR', '');
  9584. }
  9585. // Default for ext_dir
  9586. if (getenv('PHP_PEAR_EXTENSION_DIR')) {
  9587. define('PEAR_CONFIG_DEFAULT_EXT_DIR', getenv('PHP_PEAR_EXTENSION_DIR'));
  9588. } else {
  9589. if (ini_get('extension_dir')) {
  9590. define('PEAR_CONFIG_DEFAULT_EXT_DIR', ini_get('extension_dir'));
  9591. } elseif (defined('PEAR_EXTENSION_DIR') &&
  9592. file_exists(PEAR_EXTENSION_DIR) && is_dir(PEAR_EXTENSION_DIR)) {
  9593. define('PEAR_CONFIG_DEFAULT_EXT_DIR', PEAR_EXTENSION_DIR);
  9594. } elseif (defined('PHP_EXTENSION_DIR')) {
  9595. define('PEAR_CONFIG_DEFAULT_EXT_DIR', PHP_EXTENSION_DIR);
  9596. } else {
  9597. define('PEAR_CONFIG_DEFAULT_EXT_DIR', '.');
  9598. }
  9599. }
  9600. // Default for doc_dir
  9601. if (getenv('PHP_PEAR_DOC_DIR')) {
  9602. define('PEAR_CONFIG_DEFAULT_DOC_DIR', getenv('PHP_PEAR_DOC_DIR'));
  9603. } else {
  9604. define('PEAR_CONFIG_DEFAULT_DOC_DIR',
  9605. $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'docs');
  9606. }
  9607. // Default for bin_dir
  9608. if (getenv('PHP_PEAR_BIN_DIR')) {
  9609. define('PEAR_CONFIG_DEFAULT_BIN_DIR', getenv('PHP_PEAR_BIN_DIR'));
  9610. } else {
  9611. define('PEAR_CONFIG_DEFAULT_BIN_DIR', PHP_BINDIR);
  9612. }
  9613. // Default for data_dir
  9614. if (getenv('PHP_PEAR_DATA_DIR')) {
  9615. define('PEAR_CONFIG_DEFAULT_DATA_DIR', getenv('PHP_PEAR_DATA_DIR'));
  9616. } else {
  9617. define('PEAR_CONFIG_DEFAULT_DATA_DIR',
  9618. $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'data');
  9619. }
  9620. // Default for cfg_dir
  9621. if (getenv('PHP_PEAR_CFG_DIR')) {
  9622. define('PEAR_CONFIG_DEFAULT_CFG_DIR', getenv('PHP_PEAR_CFG_DIR'));
  9623. } else {
  9624. define('PEAR_CONFIG_DEFAULT_CFG_DIR',
  9625. $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'cfg');
  9626. }
  9627. // Default for www_dir
  9628. if (getenv('PHP_PEAR_WWW_DIR')) {
  9629. define('PEAR_CONFIG_DEFAULT_WWW_DIR', getenv('PHP_PEAR_WWW_DIR'));
  9630. } else {
  9631. define('PEAR_CONFIG_DEFAULT_WWW_DIR',
  9632. $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'www');
  9633. }
  9634. // Default for man_dir
  9635. if (getenv('PHP_PEAR_MAN_DIR')) {
  9636. define('PEAR_CONFIG_DEFAULT_MAN_DIR', getenv('PHP_PEAR_MAN_DIR'));
  9637. } else {
  9638. if (defined('PHP_MANDIR')) { // Added in PHP5.3.7
  9639. define('PEAR_CONFIG_DEFAULT_MAN_DIR', PHP_MANDIR);
  9640. } else {
  9641. define('PEAR_CONFIG_DEFAULT_MAN_DIR', PHP_PREFIX . DIRECTORY_SEPARATOR .
  9642. 'local' . DIRECTORY_SEPARATOR .'man');
  9643. }
  9644. }
  9645. // Default for test_dir
  9646. if (getenv('PHP_PEAR_TEST_DIR')) {
  9647. define('PEAR_CONFIG_DEFAULT_TEST_DIR', getenv('PHP_PEAR_TEST_DIR'));
  9648. } else {
  9649. define('PEAR_CONFIG_DEFAULT_TEST_DIR',
  9650. $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'tests');
  9651. }
  9652. // Default for temp_dir
  9653. if (getenv('PHP_PEAR_TEMP_DIR')) {
  9654. define('PEAR_CONFIG_DEFAULT_TEMP_DIR', getenv('PHP_PEAR_TEMP_DIR'));
  9655. } else {
  9656. define('PEAR_CONFIG_DEFAULT_TEMP_DIR',
  9657. System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
  9658. DIRECTORY_SEPARATOR . 'temp');
  9659. }
  9660. // Default for cache_dir
  9661. if (getenv('PHP_PEAR_CACHE_DIR')) {
  9662. define('PEAR_CONFIG_DEFAULT_CACHE_DIR', getenv('PHP_PEAR_CACHE_DIR'));
  9663. } else {
  9664. define('PEAR_CONFIG_DEFAULT_CACHE_DIR',
  9665. System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
  9666. DIRECTORY_SEPARATOR . 'cache');
  9667. }
  9668. // Default for download_dir
  9669. if (getenv('PHP_PEAR_DOWNLOAD_DIR')) {
  9670. define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR', getenv('PHP_PEAR_DOWNLOAD_DIR'));
  9671. } else {
  9672. define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR',
  9673. System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
  9674. DIRECTORY_SEPARATOR . 'download');
  9675. }
  9676. // Default for php_bin
  9677. if (getenv('PHP_PEAR_PHP_BIN')) {
  9678. define('PEAR_CONFIG_DEFAULT_PHP_BIN', getenv('PHP_PEAR_PHP_BIN'));
  9679. } else {
  9680. define('PEAR_CONFIG_DEFAULT_PHP_BIN', PEAR_CONFIG_DEFAULT_BIN_DIR.
  9681. DIRECTORY_SEPARATOR.'php'.(OS_WINDOWS ? '.exe' : ''));
  9682. }
  9683. // Default for verbose
  9684. if (getenv('PHP_PEAR_VERBOSE')) {
  9685. define('PEAR_CONFIG_DEFAULT_VERBOSE', getenv('PHP_PEAR_VERBOSE'));
  9686. } else {
  9687. define('PEAR_CONFIG_DEFAULT_VERBOSE', 1);
  9688. }
  9689. // Default for preferred_state
  9690. if (getenv('PHP_PEAR_PREFERRED_STATE')) {
  9691. define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', getenv('PHP_PEAR_PREFERRED_STATE'));
  9692. } else {
  9693. define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', 'stable');
  9694. }
  9695. // Default for umask
  9696. if (getenv('PHP_PEAR_UMASK')) {
  9697. define('PEAR_CONFIG_DEFAULT_UMASK', getenv('PHP_PEAR_UMASK'));
  9698. } else {
  9699. define('PEAR_CONFIG_DEFAULT_UMASK', decoct(umask()));
  9700. }
  9701. // Default for cache_ttl
  9702. if (getenv('PHP_PEAR_CACHE_TTL')) {
  9703. define('PEAR_CONFIG_DEFAULT_CACHE_TTL', getenv('PHP_PEAR_CACHE_TTL'));
  9704. } else {
  9705. define('PEAR_CONFIG_DEFAULT_CACHE_TTL', 3600);
  9706. }
  9707. // Default for sig_type
  9708. if (getenv('PHP_PEAR_SIG_TYPE')) {
  9709. define('PEAR_CONFIG_DEFAULT_SIG_TYPE', getenv('PHP_PEAR_SIG_TYPE'));
  9710. } else {
  9711. define('PEAR_CONFIG_DEFAULT_SIG_TYPE', 'gpg');
  9712. }
  9713. // Default for sig_bin
  9714. if (getenv('PHP_PEAR_SIG_BIN')) {
  9715. define('PEAR_CONFIG_DEFAULT_SIG_BIN', getenv('PHP_PEAR_SIG_BIN'));
  9716. } else {
  9717. define('PEAR_CONFIG_DEFAULT_SIG_BIN',
  9718. System::which(
  9719. 'gpg', OS_WINDOWS ? 'c:\gnupg\gpg.exe' : '/usr/local/bin/gpg'));
  9720. }
  9721. // Default for sig_keydir
  9722. if (getenv('PHP_PEAR_SIG_KEYDIR')) {
  9723. define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', getenv('PHP_PEAR_SIG_KEYDIR'));
  9724. } else {
  9725. define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR',
  9726. PEAR_CONFIG_SYSCONFDIR . DIRECTORY_SEPARATOR . 'pearkeys');
  9727. }
  9728. /**
  9729. * This is a class for storing configuration data, keeping track of
  9730. * which are system-defined, user-defined or defaulted.
  9731. * @category pear
  9732. * @package PEAR
  9733. * @author Stig Bakken <ssb@php.net>
  9734. * @author Greg Beaver <cellog@php.net>
  9735. * @copyright 1997-2009 The Authors
  9736. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  9737. * @version Release: 1.10.16
  9738. * @link http://pear.php.net/package/PEAR
  9739. * @since Class available since Release 0.1
  9740. */
  9741. class PEAR_Config extends PEAR
  9742. {
  9743. /**
  9744. * Array of config files used.
  9745. *
  9746. * @var array layer => config file
  9747. */
  9748. var $files = array(
  9749. 'system' => '',
  9750. 'user' => '',
  9751. );
  9752. var $layers = array();
  9753. /**
  9754. * Configuration data, two-dimensional array where the first
  9755. * dimension is the config layer ('user', 'system' and 'default'),
  9756. * and the second dimension is keyname => value.
  9757. *
  9758. * The order in the first dimension is important! Earlier
  9759. * layers will shadow later ones when a config value is
  9760. * requested (if a 'user' value exists, it will be returned first,
  9761. * then 'system' and finally 'default').
  9762. *
  9763. * @var array layer => array(keyname => value, ...)
  9764. */
  9765. var $configuration = array(
  9766. 'user' => array(),
  9767. 'system' => array(),
  9768. 'default' => array(),
  9769. );
  9770. /**
  9771. * Configuration values that can be set for a channel
  9772. *
  9773. * All other configuration values can only have a global value
  9774. * @var array
  9775. * @access private
  9776. */
  9777. var $_channelConfigInfo = array(
  9778. 'php_dir', 'ext_dir', 'doc_dir', 'bin_dir', 'data_dir', 'cfg_dir',
  9779. 'test_dir', 'www_dir', 'php_bin', 'php_prefix', 'php_suffix', 'username',
  9780. 'password', 'verbose', 'preferred_state', 'umask', 'preferred_mirror', 'php_ini'
  9781. );
  9782. /**
  9783. * Channels that can be accessed
  9784. * @see setChannels()
  9785. * @var array
  9786. * @access private
  9787. */
  9788. var $_channels = array('pear.php.net', 'pecl.php.net', '__uri');
  9789. /**
  9790. * This variable is used to control the directory values returned
  9791. * @see setInstallRoot();
  9792. * @var string|false
  9793. * @access private
  9794. */
  9795. var $_installRoot = false;
  9796. /**
  9797. * If requested, this will always refer to the registry
  9798. * contained in php_dir
  9799. * @var PEAR_Registry
  9800. */
  9801. var $_registry = array();
  9802. /**
  9803. * @var array
  9804. * @access private
  9805. */
  9806. var $_regInitialized = array();
  9807. /**
  9808. * @var bool
  9809. * @access private
  9810. */
  9811. var $_noRegistry = false;
  9812. /**
  9813. * amount of errors found while parsing config
  9814. * @var integer
  9815. * @access private
  9816. */
  9817. var $_errorsFound = 0;
  9818. var $_lastError = null;
  9819. /**
  9820. * Information about the configuration data. Stores the type,
  9821. * default value and a documentation string for each configuration
  9822. * value.
  9823. *
  9824. * @var array layer => array(infotype => value, ...)
  9825. */
  9826. var $configuration_info = array(
  9827. // Channels/Internet Access
  9828. 'default_channel' => array(
  9829. 'type' => 'string',
  9830. 'default' => PEAR_CONFIG_DEFAULT_CHANNEL,
  9831. 'doc' => 'the default channel to use for all non explicit commands',
  9832. 'prompt' => 'Default Channel',
  9833. 'group' => 'Internet Access',
  9834. ),
  9835. 'preferred_mirror' => array(
  9836. 'type' => 'string',
  9837. 'default' => PEAR_CONFIG_DEFAULT_CHANNEL,
  9838. 'doc' => 'the default server or mirror to use for channel actions',
  9839. 'prompt' => 'Default Channel Mirror',
  9840. 'group' => 'Internet Access',
  9841. ),
  9842. 'remote_config' => array(
  9843. 'type' => 'password',
  9844. 'default' => '',
  9845. 'doc' => 'ftp url of remote configuration file to use for synchronized install',
  9846. 'prompt' => 'Remote Configuration File',
  9847. 'group' => 'Internet Access',
  9848. ),
  9849. 'auto_discover' => array(
  9850. 'type' => 'integer',
  9851. 'default' => 0,
  9852. 'doc' => 'whether to automatically discover new channels',
  9853. 'prompt' => 'Auto-discover new Channels',
  9854. 'group' => 'Internet Access',
  9855. ),
  9856. // Internet Access
  9857. 'master_server' => array(
  9858. 'type' => 'string',
  9859. 'default' => 'pear.php.net',
  9860. 'doc' => 'name of the main PEAR server [NOT USED IN THIS VERSION]',
  9861. 'prompt' => 'PEAR server [DEPRECATED]',
  9862. 'group' => 'Internet Access',
  9863. ),
  9864. 'http_proxy' => array(
  9865. 'type' => 'string',
  9866. 'default' => PEAR_CONFIG_DEFAULT_HTTP_PROXY,
  9867. 'doc' => 'HTTP proxy (host:port) to use when downloading packages',
  9868. 'prompt' => 'HTTP Proxy Server Address',
  9869. 'group' => 'Internet Access',
  9870. ),
  9871. // File Locations
  9872. 'php_dir' => array(
  9873. 'type' => 'directory',
  9874. 'default' => PEAR_CONFIG_DEFAULT_PHP_DIR,
  9875. 'doc' => 'directory where .php files are installed',
  9876. 'prompt' => 'PEAR directory',
  9877. 'group' => 'File Locations',
  9878. ),
  9879. 'ext_dir' => array(
  9880. 'type' => 'directory',
  9881. 'default' => PEAR_CONFIG_DEFAULT_EXT_DIR,
  9882. 'doc' => 'directory where loadable extensions are installed',
  9883. 'prompt' => 'PHP extension directory',
  9884. 'group' => 'File Locations',
  9885. ),
  9886. 'doc_dir' => array(
  9887. 'type' => 'directory',
  9888. 'default' => PEAR_CONFIG_DEFAULT_DOC_DIR,
  9889. 'doc' => 'directory where documentation is installed',
  9890. 'prompt' => 'PEAR documentation directory',
  9891. 'group' => 'File Locations',
  9892. ),
  9893. 'bin_dir' => array(
  9894. 'type' => 'directory',
  9895. 'default' => PEAR_CONFIG_DEFAULT_BIN_DIR,
  9896. 'doc' => 'directory where executables are installed',
  9897. 'prompt' => 'PEAR executables directory',
  9898. 'group' => 'File Locations',
  9899. ),
  9900. 'data_dir' => array(
  9901. 'type' => 'directory',
  9902. 'default' => PEAR_CONFIG_DEFAULT_DATA_DIR,
  9903. 'doc' => 'directory where data files are installed',
  9904. 'prompt' => 'PEAR data directory',
  9905. 'group' => 'File Locations (Advanced)',
  9906. ),
  9907. 'cfg_dir' => array(
  9908. 'type' => 'directory',
  9909. 'default' => PEAR_CONFIG_DEFAULT_CFG_DIR,
  9910. 'doc' => 'directory where modifiable configuration files are installed',
  9911. 'prompt' => 'PEAR configuration file directory',
  9912. 'group' => 'File Locations (Advanced)',
  9913. ),
  9914. 'www_dir' => array(
  9915. 'type' => 'directory',
  9916. 'default' => PEAR_CONFIG_DEFAULT_WWW_DIR,
  9917. 'doc' => 'directory where www frontend files (html/js) are installed',
  9918. 'prompt' => 'PEAR www files directory',
  9919. 'group' => 'File Locations (Advanced)',
  9920. ),
  9921. 'man_dir' => array(
  9922. 'type' => 'directory',
  9923. 'default' => PEAR_CONFIG_DEFAULT_MAN_DIR,
  9924. 'doc' => 'directory where unix manual pages are installed',
  9925. 'prompt' => 'Systems manpage files directory',
  9926. 'group' => 'File Locations (Advanced)',
  9927. ),
  9928. 'test_dir' => array(
  9929. 'type' => 'directory',
  9930. 'default' => PEAR_CONFIG_DEFAULT_TEST_DIR,
  9931. 'doc' => 'directory where regression tests are installed',
  9932. 'prompt' => 'PEAR test directory',
  9933. 'group' => 'File Locations (Advanced)',
  9934. ),
  9935. 'cache_dir' => array(
  9936. 'type' => 'directory',
  9937. 'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR,
  9938. 'doc' => 'directory which is used for web service cache',
  9939. 'prompt' => 'PEAR Installer cache directory',
  9940. 'group' => 'File Locations (Advanced)',
  9941. ),
  9942. 'temp_dir' => array(
  9943. 'type' => 'directory',
  9944. 'default' => PEAR_CONFIG_DEFAULT_TEMP_DIR,
  9945. 'doc' => 'directory which is used for all temp files',
  9946. 'prompt' => 'PEAR Installer temp directory',
  9947. 'group' => 'File Locations (Advanced)',
  9948. ),
  9949. 'download_dir' => array(
  9950. 'type' => 'directory',
  9951. 'default' => PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR,
  9952. 'doc' => 'directory which is used for all downloaded files',
  9953. 'prompt' => 'PEAR Installer download directory',
  9954. 'group' => 'File Locations (Advanced)',
  9955. ),
  9956. 'php_bin' => array(
  9957. 'type' => 'file',
  9958. 'default' => PEAR_CONFIG_DEFAULT_PHP_BIN,
  9959. 'doc' => 'PHP CLI/CGI binary for executing scripts',
  9960. 'prompt' => 'PHP CLI/CGI binary',
  9961. 'group' => 'File Locations (Advanced)',
  9962. ),
  9963. 'php_prefix' => array(
  9964. 'type' => 'string',
  9965. 'default' => '',
  9966. 'doc' => '--program-prefix for php_bin\'s ./configure, used for pecl installs',
  9967. 'prompt' => '--program-prefix passed to PHP\'s ./configure',
  9968. 'group' => 'File Locations (Advanced)',
  9969. ),
  9970. 'php_suffix' => array(
  9971. 'type' => 'string',
  9972. 'default' => '',
  9973. 'doc' => '--program-suffix for php_bin\'s ./configure, used for pecl installs',
  9974. 'prompt' => '--program-suffix passed to PHP\'s ./configure',
  9975. 'group' => 'File Locations (Advanced)',
  9976. ),
  9977. 'php_ini' => array(
  9978. 'type' => 'file',
  9979. 'default' => '',
  9980. 'doc' => 'location of php.ini in which to enable PECL extensions on install',
  9981. 'prompt' => 'php.ini location',
  9982. 'group' => 'File Locations (Advanced)',
  9983. ),
  9984. 'metadata_dir' => array(
  9985. 'type' => 'directory',
  9986. 'default' => PEAR_CONFIG_DEFAULT_METADATA_DIR,
  9987. 'doc' => 'directory where metadata files are installed (registry, filemap, channels, ...)',
  9988. 'prompt' => 'PEAR metadata directory',
  9989. 'group' => 'File Locations (Advanced)',
  9990. ),
  9991. // Maintainers
  9992. 'username' => array(
  9993. 'type' => 'string',
  9994. 'default' => '',
  9995. 'doc' => '(maintainers) your PEAR account name',
  9996. 'prompt' => 'PEAR username (for maintainers)',
  9997. 'group' => 'Maintainers',
  9998. ),
  9999. 'password' => array(
  10000. 'type' => 'password',
  10001. 'default' => '',
  10002. 'doc' => '(maintainers) your PEAR account password',
  10003. 'prompt' => 'PEAR password (for maintainers)',
  10004. 'group' => 'Maintainers',
  10005. ),
  10006. // Advanced
  10007. 'verbose' => array(
  10008. 'type' => 'integer',
  10009. 'default' => PEAR_CONFIG_DEFAULT_VERBOSE,
  10010. 'doc' => 'verbosity level
  10011. 0: really quiet
  10012. 1: somewhat quiet
  10013. 2: verbose
  10014. 3: debug',
  10015. 'prompt' => 'Debug Log Level',
  10016. 'group' => 'Advanced',
  10017. ),
  10018. 'preferred_state' => array(
  10019. 'type' => 'set',
  10020. 'default' => PEAR_CONFIG_DEFAULT_PREFERRED_STATE,
  10021. 'doc' => 'the installer will prefer releases with this state when installing packages without a version or state specified',
  10022. 'valid_set' => array(
  10023. 'stable', 'beta', 'alpha', 'devel', 'snapshot'),
  10024. 'prompt' => 'Preferred Package State',
  10025. 'group' => 'Advanced',
  10026. ),
  10027. 'umask' => array(
  10028. 'type' => 'mask',
  10029. 'default' => PEAR_CONFIG_DEFAULT_UMASK,
  10030. 'doc' => 'umask used when creating files (Unix-like systems only)',
  10031. 'prompt' => 'Unix file mask',
  10032. 'group' => 'Advanced',
  10033. ),
  10034. 'cache_ttl' => array(
  10035. 'type' => 'integer',
  10036. 'default' => PEAR_CONFIG_DEFAULT_CACHE_TTL,
  10037. 'doc' => 'amount of secs where the local cache is used and not updated',
  10038. 'prompt' => 'Cache TimeToLive',
  10039. 'group' => 'Advanced',
  10040. ),
  10041. 'sig_type' => array(
  10042. 'type' => 'set',
  10043. 'default' => PEAR_CONFIG_DEFAULT_SIG_TYPE,
  10044. 'doc' => 'which package signature mechanism to use',
  10045. 'valid_set' => array('gpg'),
  10046. 'prompt' => 'Package Signature Type',
  10047. 'group' => 'Maintainers',
  10048. ),
  10049. 'sig_bin' => array(
  10050. 'type' => 'string',
  10051. 'default' => PEAR_CONFIG_DEFAULT_SIG_BIN,
  10052. 'doc' => 'which package signature mechanism to use',
  10053. 'prompt' => 'Signature Handling Program',
  10054. 'group' => 'Maintainers',
  10055. ),
  10056. 'sig_keyid' => array(
  10057. 'type' => 'string',
  10058. 'default' => '',
  10059. 'doc' => 'which key to use for signing with',
  10060. 'prompt' => 'Signature Key Id',
  10061. 'group' => 'Maintainers',
  10062. ),
  10063. 'sig_keydir' => array(
  10064. 'type' => 'directory',
  10065. 'default' => PEAR_CONFIG_DEFAULT_SIG_KEYDIR,
  10066. 'doc' => 'directory where signature keys are located',
  10067. 'prompt' => 'Signature Key Directory',
  10068. 'group' => 'Maintainers',
  10069. ),
  10070. // __channels is reserved - used for channel-specific configuration
  10071. );
  10072. /**
  10073. * Constructor.
  10074. *
  10075. * @param string file to read user-defined options from
  10076. * @param string file to read system-wide defaults from
  10077. * @param bool determines whether a registry object "follows"
  10078. * the value of php_dir (is automatically created
  10079. * and moved when php_dir is changed)
  10080. * @param bool if true, fails if configuration files cannot be loaded
  10081. *
  10082. * @access public
  10083. *
  10084. * @see PEAR_Config::singleton
  10085. */
  10086. function __construct($user_file = '', $system_file = '', $ftp_file = false,
  10087. $strict = true)
  10088. {
  10089. parent::__construct();
  10090. PEAR_Installer_Role::initializeConfig($this);
  10091. $sl = DIRECTORY_SEPARATOR;
  10092. if (empty($user_file)) {
  10093. if (OS_WINDOWS) {
  10094. $user_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini';
  10095. } else {
  10096. $user_file = getenv('HOME') . $sl . '.pearrc';
  10097. }
  10098. }
  10099. if (empty($system_file)) {
  10100. $system_file = PEAR_CONFIG_SYSCONFDIR . $sl;
  10101. if (OS_WINDOWS) {
  10102. $system_file .= 'pearsys.ini';
  10103. } else {
  10104. $system_file .= 'pear.conf';
  10105. }
  10106. }
  10107. $this->layers = array_keys($this->configuration);
  10108. $this->files['user'] = $user_file;
  10109. $this->files['system'] = $system_file;
  10110. if ($user_file && file_exists($user_file)) {
  10111. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  10112. $this->readConfigFile($user_file, 'user', $strict);
  10113. $this->popErrorHandling();
  10114. if ($this->_errorsFound > 0) {
  10115. return;
  10116. }
  10117. }
  10118. if ($system_file && @file_exists($system_file)) {
  10119. $this->mergeConfigFile($system_file, false, 'system', $strict);
  10120. if ($this->_errorsFound > 0) {
  10121. return;
  10122. }
  10123. }
  10124. if (!$ftp_file) {
  10125. $ftp_file = $this->get('remote_config');
  10126. }
  10127. if ($ftp_file && defined('PEAR_REMOTEINSTALL_OK')) {
  10128. $this->readFTPConfigFile($ftp_file);
  10129. }
  10130. foreach ($this->configuration_info as $key => $info) {
  10131. $this->configuration['default'][$key] = $info['default'];
  10132. }
  10133. $this->_registry['default'] = new PEAR_Registry(
  10134. $this->configuration['default']['php_dir'], false, false,
  10135. $this->configuration['default']['metadata_dir']);
  10136. $this->_registry['default']->setConfig($this, false);
  10137. $this->_regInitialized['default'] = false;
  10138. //$GLOBALS['_PEAR_Config_instance'] = &$this;
  10139. }
  10140. /**
  10141. * Return the default locations of user and system configuration files
  10142. */
  10143. public static function getDefaultConfigFiles()
  10144. {
  10145. $sl = DIRECTORY_SEPARATOR;
  10146. if (OS_WINDOWS) {
  10147. return array(
  10148. 'user' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini',
  10149. 'system' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini'
  10150. );
  10151. }
  10152. return array(
  10153. 'user' => getenv('HOME') . $sl . '.pearrc',
  10154. 'system' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf'
  10155. );
  10156. }
  10157. /**
  10158. * Static singleton method. If you want to keep only one instance
  10159. * of this class in use, this method will give you a reference to
  10160. * the last created PEAR_Config object if one exists, or create a
  10161. * new object.
  10162. *
  10163. * @param string (optional) file to read user-defined options from
  10164. * @param string (optional) file to read system-wide defaults from
  10165. *
  10166. * @return object an existing or new PEAR_Config instance
  10167. *
  10168. * @see PEAR_Config::PEAR_Config
  10169. */
  10170. public static function &singleton($user_file = '', $system_file = '', $strict = true)
  10171. {
  10172. if (is_object($GLOBALS['_PEAR_Config_instance'])) {
  10173. return $GLOBALS['_PEAR_Config_instance'];
  10174. }
  10175. $t_conf = new PEAR_Config($user_file, $system_file, false, $strict);
  10176. if ($t_conf->_errorsFound > 0) {
  10177. return $t_conf->_lastError;
  10178. }
  10179. $GLOBALS['_PEAR_Config_instance'] = &$t_conf;
  10180. return $GLOBALS['_PEAR_Config_instance'];
  10181. }
  10182. /**
  10183. * Determine whether any configuration files have been detected, and whether a
  10184. * registry object can be retrieved from this configuration.
  10185. * @return bool
  10186. * @since PEAR 1.4.0a1
  10187. */
  10188. function validConfiguration()
  10189. {
  10190. if ($this->isDefinedLayer('user') || $this->isDefinedLayer('system')) {
  10191. return true;
  10192. }
  10193. return false;
  10194. }
  10195. /**
  10196. * Reads configuration data from a file. All existing values in
  10197. * the config layer are discarded and replaced with data from the
  10198. * file.
  10199. * @param string file to read from, if NULL or not specified, the
  10200. * last-used file for the same layer (second param) is used
  10201. * @param string config layer to insert data into ('user' or 'system')
  10202. * @return bool TRUE on success or a PEAR error on failure
  10203. */
  10204. function readConfigFile($file = null, $layer = 'user', $strict = true)
  10205. {
  10206. if (empty($this->files[$layer])) {
  10207. return $this->raiseError("unknown config layer `$layer'");
  10208. }
  10209. if ($file === null) {
  10210. $file = $this->files[$layer];
  10211. }
  10212. $data = $this->_readConfigDataFrom($file);
  10213. if (PEAR::isError($data)) {
  10214. if (!$strict) {
  10215. return true;
  10216. }
  10217. $this->_errorsFound++;
  10218. $this->_lastError = $data;
  10219. return $data;
  10220. }
  10221. $this->files[$layer] = $file;
  10222. $this->_decodeInput($data);
  10223. $this->configuration[$layer] = $data;
  10224. $this->_setupChannels();
  10225. if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) {
  10226. $this->_registry[$layer] = new PEAR_Registry(
  10227. $phpdir, false, false,
  10228. $this->get('metadata_dir', $layer, 'pear.php.net'));
  10229. $this->_registry[$layer]->setConfig($this, false);
  10230. $this->_regInitialized[$layer] = false;
  10231. } else {
  10232. unset($this->_registry[$layer]);
  10233. }
  10234. return true;
  10235. }
  10236. /**
  10237. * @param string url to the remote config file, like ftp://www.example.com/pear/config.ini
  10238. * @return true|PEAR_Error
  10239. */
  10240. function readFTPConfigFile($path)
  10241. {
  10242. do { // poor man's try
  10243. if (!class_exists('PEAR_FTP')) {
  10244. if (!class_exists('PEAR_Common')) {
  10245. require_once 'phar://go-pear.phar/' . 'PEAR/Common.php';
  10246. }
  10247. if (PEAR_Common::isIncludeable('PEAR/FTP.php')) {
  10248. require_once 'phar://go-pear.phar/' . 'PEAR/FTP.php';
  10249. }
  10250. }
  10251. if (!class_exists('PEAR_FTP')) {
  10252. return PEAR::raiseError('PEAR_RemoteInstaller must be installed to use remote config');
  10253. }
  10254. $this->_ftp = new PEAR_FTP;
  10255. $this->_ftp->pushErrorHandling(PEAR_ERROR_RETURN);
  10256. $e = $this->_ftp->init($path);
  10257. if (PEAR::isError($e)) {
  10258. $this->_ftp->popErrorHandling();
  10259. return $e;
  10260. }
  10261. $tmp = System::mktemp('-d');
  10262. PEAR_Common::addTempFile($tmp);
  10263. $e = $this->_ftp->get(basename($path), $tmp . DIRECTORY_SEPARATOR .
  10264. 'pear.ini', false, FTP_BINARY);
  10265. if (PEAR::isError($e)) {
  10266. $this->_ftp->popErrorHandling();
  10267. return $e;
  10268. }
  10269. PEAR_Common::addTempFile($tmp . DIRECTORY_SEPARATOR . 'pear.ini');
  10270. $this->_ftp->disconnect();
  10271. $this->_ftp->popErrorHandling();
  10272. $this->files['ftp'] = $tmp . DIRECTORY_SEPARATOR . 'pear.ini';
  10273. $e = $this->readConfigFile(null, 'ftp');
  10274. if (PEAR::isError($e)) {
  10275. return $e;
  10276. }
  10277. $fail = array();
  10278. foreach ($this->configuration_info as $key => $val) {
  10279. if (in_array($this->getGroup($key),
  10280. array('File Locations', 'File Locations (Advanced)')) &&
  10281. $this->getType($key) == 'directory') {
  10282. // any directory configs must be set for this to work
  10283. if (!isset($this->configuration['ftp'][$key])) {
  10284. $fail[] = $key;
  10285. }
  10286. }
  10287. }
  10288. if (!count($fail)) {
  10289. return true;
  10290. }
  10291. $fail = '"' . implode('", "', $fail) . '"';
  10292. unset($this->files['ftp']);
  10293. unset($this->configuration['ftp']);
  10294. return PEAR::raiseError('ERROR: Ftp configuration file must set all ' .
  10295. 'directory configuration variables. These variables were not set: ' .
  10296. $fail);
  10297. } while (false); // poor man's catch
  10298. unset($this->files['ftp']);
  10299. return PEAR::raiseError('no remote host specified');
  10300. }
  10301. /**
  10302. * Reads the existing configurations and creates the _channels array from it
  10303. */
  10304. function _setupChannels()
  10305. {
  10306. $set = array_flip(array_values($this->_channels));
  10307. foreach ($this->configuration as $layer => $data) {
  10308. $i = 1000;
  10309. if (isset($data['__channels']) && is_array($data['__channels'])) {
  10310. foreach ($data['__channels'] as $channel => $info) {
  10311. $set[$channel] = $i++;
  10312. }
  10313. }
  10314. }
  10315. $this->_channels = array_values(array_flip($set));
  10316. $this->setChannels($this->_channels);
  10317. }
  10318. function deleteChannel($channel)
  10319. {
  10320. $ch = strtolower($channel);
  10321. foreach ($this->configuration as $layer => $data) {
  10322. if (isset($data['__channels']) && isset($data['__channels'][$ch])) {
  10323. unset($this->configuration[$layer]['__channels'][$ch]);
  10324. }
  10325. }
  10326. $this->_channels = array_flip($this->_channels);
  10327. unset($this->_channels[$ch]);
  10328. $this->_channels = array_flip($this->_channels);
  10329. }
  10330. /**
  10331. * Merges data into a config layer from a file. Does the same
  10332. * thing as readConfigFile, except it does not replace all
  10333. * existing values in the config layer.
  10334. * @param string file to read from
  10335. * @param bool whether to overwrite existing data (default TRUE)
  10336. * @param string config layer to insert data into ('user' or 'system')
  10337. * @param string if true, errors are returned if file opening fails
  10338. * @return bool TRUE on success or a PEAR error on failure
  10339. */
  10340. function mergeConfigFile($file, $override = true, $layer = 'user', $strict = true)
  10341. {
  10342. if (empty($this->files[$layer])) {
  10343. return $this->raiseError("unknown config layer `$layer'");
  10344. }
  10345. if ($file === null) {
  10346. $file = $this->files[$layer];
  10347. }
  10348. $data = $this->_readConfigDataFrom($file);
  10349. if (PEAR::isError($data)) {
  10350. if (!$strict) {
  10351. return true;
  10352. }
  10353. $this->_errorsFound++;
  10354. $this->_lastError = $data;
  10355. return $data;
  10356. }
  10357. $this->_decodeInput($data);
  10358. if ($override) {
  10359. $this->configuration[$layer] =
  10360. PEAR_Config::arrayMergeRecursive($this->configuration[$layer], $data);
  10361. } else {
  10362. $this->configuration[$layer] =
  10363. PEAR_Config::arrayMergeRecursive($data, $this->configuration[$layer]);
  10364. }
  10365. $this->_setupChannels();
  10366. if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) {
  10367. $this->_registry[$layer] = new PEAR_Registry(
  10368. $phpdir, false, false,
  10369. $this->get('metadata_dir', $layer, 'pear.php.net'));
  10370. $this->_registry[$layer]->setConfig($this, false);
  10371. $this->_regInitialized[$layer] = false;
  10372. } else {
  10373. unset($this->_registry[$layer]);
  10374. }
  10375. return true;
  10376. }
  10377. /**
  10378. * @param array
  10379. * @param array
  10380. * @return array
  10381. */
  10382. public static function arrayMergeRecursive($arr2, $arr1)
  10383. {
  10384. $ret = array();
  10385. foreach ($arr2 as $key => $data) {
  10386. if (!isset($arr1[$key])) {
  10387. $ret[$key] = $data;
  10388. unset($arr1[$key]);
  10389. continue;
  10390. }
  10391. if (is_array($data)) {
  10392. if (!is_array($arr1[$key])) {
  10393. $ret[$key] = $arr1[$key];
  10394. unset($arr1[$key]);
  10395. continue;
  10396. }
  10397. $ret[$key] = PEAR_Config::arrayMergeRecursive($arr1[$key], $arr2[$key]);
  10398. unset($arr1[$key]);
  10399. }
  10400. }
  10401. return array_merge($ret, $arr1);
  10402. }
  10403. /**
  10404. * Writes data into a config layer from a file.
  10405. *
  10406. * @param string|null file to read from, or null for default
  10407. * @param string config layer to insert data into ('user' or
  10408. * 'system')
  10409. * @param string|null data to write to config file or null for internal data [DEPRECATED]
  10410. * @return bool TRUE on success or a PEAR error on failure
  10411. */
  10412. function writeConfigFile($file = null, $layer = 'user', $data = null)
  10413. {
  10414. $this->_lazyChannelSetup($layer);
  10415. if ($layer == 'both' || $layer == 'all') {
  10416. foreach ($this->files as $type => $file) {
  10417. $err = $this->writeConfigFile($file, $type, $data);
  10418. if (PEAR::isError($err)) {
  10419. return $err;
  10420. }
  10421. }
  10422. return true;
  10423. }
  10424. if (empty($this->files[$layer])) {
  10425. return $this->raiseError("unknown config file type `$layer'");
  10426. }
  10427. if ($file === null) {
  10428. $file = $this->files[$layer];
  10429. }
  10430. $data = ($data === null) ? $this->configuration[$layer] : $data;
  10431. $this->_encodeOutput($data);
  10432. $opt = array('-p', dirname($file));
  10433. if (!@System::mkDir($opt)) {
  10434. return $this->raiseError("could not create directory: " . dirname($file));
  10435. }
  10436. if (file_exists($file) && is_file($file) && !is_writeable($file)) {
  10437. return $this->raiseError("no write access to $file!");
  10438. }
  10439. $fp = @fopen($file, "w");
  10440. if (!$fp) {
  10441. return $this->raiseError("PEAR_Config::writeConfigFile fopen('$file','w') failed ($php_errormsg)");
  10442. }
  10443. $contents = "#PEAR_Config 0.9\n" . serialize($data);
  10444. if (!@fwrite($fp, $contents)) {
  10445. return $this->raiseError("PEAR_Config::writeConfigFile: fwrite failed ($php_errormsg)");
  10446. }
  10447. return true;
  10448. }
  10449. /**
  10450. * Reads configuration data from a file and returns the parsed data
  10451. * in an array.
  10452. *
  10453. * @param string file to read from
  10454. * @return array configuration data or a PEAR error on failure
  10455. * @access private
  10456. */
  10457. function _readConfigDataFrom($file)
  10458. {
  10459. $fp = false;
  10460. if (file_exists($file)) {
  10461. $fp = @fopen($file, "r");
  10462. }
  10463. if (!$fp) {
  10464. return $this->raiseError("PEAR_Config::readConfigFile fopen('$file','r') failed");
  10465. }
  10466. $size = filesize($file);
  10467. fclose($fp);
  10468. $contents = file_get_contents($file);
  10469. if (empty($contents)) {
  10470. return $this->raiseError('Configuration file "' . $file . '" is empty');
  10471. }
  10472. $version = false;
  10473. if (preg_match('/^#PEAR_Config\s+(\S+)\s+/si', $contents, $matches)) {
  10474. $version = $matches[1];
  10475. $contents = substr($contents, strlen($matches[0]));
  10476. } else {
  10477. // Museum config file
  10478. if (substr($contents,0,2) == 'a:') {
  10479. $version = '0.1';
  10480. }
  10481. }
  10482. if ($version && version_compare("$version", '1', '<')) {
  10483. $data = @unserialize($contents);
  10484. if (!is_array($data) && !$data) {
  10485. if ($contents == serialize(false)) {
  10486. $data = array();
  10487. } else {
  10488. $err = $this->raiseError("PEAR_Config: bad data in $file");
  10489. return $err;
  10490. }
  10491. }
  10492. if (!is_array($data)) {
  10493. if (strlen(trim($contents)) > 0) {
  10494. $error = "PEAR_Config: bad data in $file";
  10495. $err = $this->raiseError($error);
  10496. return $err;
  10497. }
  10498. $data = array();
  10499. }
  10500. // add parsing of newer formats here...
  10501. } else {
  10502. $err = $this->raiseError("$file: unknown version `$version'");
  10503. return $err;
  10504. }
  10505. return $data;
  10506. }
  10507. /**
  10508. * Gets the file used for storing the config for a layer
  10509. *
  10510. * @param string $layer 'user' or 'system'
  10511. */
  10512. function getConfFile($layer)
  10513. {
  10514. return $this->files[$layer];
  10515. }
  10516. /**
  10517. * @param string Configuration class name, used for detecting duplicate calls
  10518. * @param array information on a role as parsed from its xml file
  10519. * @return true|PEAR_Error
  10520. * @access private
  10521. */
  10522. function _addConfigVars($class, $vars)
  10523. {
  10524. static $called = array();
  10525. if (isset($called[$class])) {
  10526. return;
  10527. }
  10528. $called[$class] = 1;
  10529. if (count($vars) > 3) {
  10530. return $this->raiseError('Roles can only define 3 new config variables or less');
  10531. }
  10532. foreach ($vars as $name => $var) {
  10533. if (!is_array($var)) {
  10534. return $this->raiseError('Configuration information must be an array');
  10535. }
  10536. if (!isset($var['type'])) {
  10537. return $this->raiseError('Configuration information must contain a type');
  10538. } elseif (!in_array($var['type'],
  10539. array('string', 'mask', 'password', 'directory', 'file', 'set'))) {
  10540. return $this->raiseError(
  10541. 'Configuration type must be one of directory, file, string, ' .
  10542. 'mask, set, or password');
  10543. }
  10544. if (!isset($var['default'])) {
  10545. return $this->raiseError(
  10546. 'Configuration information must contain a default value ("default" index)');
  10547. }
  10548. if (is_array($var['default'])) {
  10549. $real_default = '';
  10550. foreach ($var['default'] as $config_var => $val) {
  10551. if (strpos($config_var, 'text') === 0) {
  10552. $real_default .= $val;
  10553. } elseif (strpos($config_var, 'constant') === 0) {
  10554. if (!defined($val)) {
  10555. return $this->raiseError(
  10556. 'Unknown constant "' . $val . '" requested in ' .
  10557. 'default value for configuration variable "' .
  10558. $name . '"');
  10559. }
  10560. $real_default .= constant($val);
  10561. } elseif (isset($this->configuration_info[$config_var])) {
  10562. $real_default .=
  10563. $this->configuration_info[$config_var]['default'];
  10564. } else {
  10565. return $this->raiseError(
  10566. 'Unknown request for "' . $config_var . '" value in ' .
  10567. 'default value for configuration variable "' .
  10568. $name . '"');
  10569. }
  10570. }
  10571. $var['default'] = $real_default;
  10572. }
  10573. if ($var['type'] == 'integer') {
  10574. $var['default'] = (integer) $var['default'];
  10575. }
  10576. if (!isset($var['doc'])) {
  10577. return $this->raiseError(
  10578. 'Configuration information must contain a summary ("doc" index)');
  10579. }
  10580. if (!isset($var['prompt'])) {
  10581. return $this->raiseError(
  10582. 'Configuration information must contain a simple prompt ("prompt" index)');
  10583. }
  10584. if (!isset($var['group'])) {
  10585. return $this->raiseError(
  10586. 'Configuration information must contain a simple group ("group" index)');
  10587. }
  10588. if (isset($this->configuration_info[$name])) {
  10589. return $this->raiseError('Configuration variable "' . $name .
  10590. '" already exists');
  10591. }
  10592. $this->configuration_info[$name] = $var;
  10593. // fix bug #7351: setting custom config variable in a channel fails
  10594. $this->_channelConfigInfo[] = $name;
  10595. }
  10596. return true;
  10597. }
  10598. /**
  10599. * Encodes/scrambles configuration data before writing to files.
  10600. * Currently, 'password' values will be base64-encoded as to avoid
  10601. * that people spot cleartext passwords by accident.
  10602. *
  10603. * @param array (reference) array to encode values in
  10604. * @return bool TRUE on success
  10605. * @access private
  10606. */
  10607. function _encodeOutput(&$data)
  10608. {
  10609. foreach ($data as $key => $value) {
  10610. if ($key == '__channels') {
  10611. foreach ($data['__channels'] as $channel => $blah) {
  10612. $this->_encodeOutput($data['__channels'][$channel]);
  10613. }
  10614. }
  10615. if (!isset($this->configuration_info[$key])) {
  10616. continue;
  10617. }
  10618. $type = $this->configuration_info[$key]['type'];
  10619. switch ($type) {
  10620. // we base64-encode passwords so they are at least
  10621. // not shown in plain by accident
  10622. case 'password': {
  10623. $data[$key] = base64_encode($data[$key]);
  10624. break;
  10625. }
  10626. case 'mask': {
  10627. $data[$key] = octdec($data[$key]);
  10628. break;
  10629. }
  10630. }
  10631. }
  10632. return true;
  10633. }
  10634. /**
  10635. * Decodes/unscrambles configuration data after reading from files.
  10636. *
  10637. * @param array (reference) array to encode values in
  10638. * @return bool TRUE on success
  10639. * @access private
  10640. *
  10641. * @see PEAR_Config::_encodeOutput
  10642. */
  10643. function _decodeInput(&$data)
  10644. {
  10645. if (!is_array($data)) {
  10646. return true;
  10647. }
  10648. foreach ($data as $key => $value) {
  10649. if ($key == '__channels') {
  10650. foreach ($data['__channels'] as $channel => $blah) {
  10651. $this->_decodeInput($data['__channels'][$channel]);
  10652. }
  10653. }
  10654. if (!isset($this->configuration_info[$key])) {
  10655. continue;
  10656. }
  10657. $type = $this->configuration_info[$key]['type'];
  10658. switch ($type) {
  10659. case 'password': {
  10660. $data[$key] = base64_decode($data[$key]);
  10661. break;
  10662. }
  10663. case 'mask': {
  10664. $data[$key] = decoct($data[$key]);
  10665. break;
  10666. }
  10667. }
  10668. }
  10669. return true;
  10670. }
  10671. /**
  10672. * Retrieve the default channel.
  10673. *
  10674. * On startup, channels are not initialized, so if the default channel is not
  10675. * pear.php.net, then initialize the config.
  10676. * @param string registry layer
  10677. * @return string|false
  10678. */
  10679. function getDefaultChannel($layer = null)
  10680. {
  10681. $ret = false;
  10682. if ($layer === null) {
  10683. foreach ($this->layers as $layer) {
  10684. if (isset($this->configuration[$layer]['default_channel'])) {
  10685. $ret = $this->configuration[$layer]['default_channel'];
  10686. break;
  10687. }
  10688. }
  10689. } elseif (isset($this->configuration[$layer]['default_channel'])) {
  10690. $ret = $this->configuration[$layer]['default_channel'];
  10691. }
  10692. if ($ret == 'pear.php.net' && defined('PEAR_RUNTYPE') && PEAR_RUNTYPE == 'pecl') {
  10693. $ret = 'pecl.php.net';
  10694. }
  10695. if ($ret) {
  10696. if ($ret != 'pear.php.net') {
  10697. $this->_lazyChannelSetup();
  10698. }
  10699. return $ret;
  10700. }
  10701. return PEAR_CONFIG_DEFAULT_CHANNEL;
  10702. }
  10703. /**
  10704. * Returns a configuration value, prioritizing layers as per the
  10705. * layers property.
  10706. *
  10707. * @param string config key
  10708. * @return mixed the config value, or NULL if not found
  10709. * @access public
  10710. */
  10711. function get($key, $layer = null, $channel = false)
  10712. {
  10713. if (!isset($this->configuration_info[$key])) {
  10714. return null;
  10715. }
  10716. if ($key == '__channels') {
  10717. return null;
  10718. }
  10719. if ($key == 'default_channel') {
  10720. return $this->getDefaultChannel($layer);
  10721. }
  10722. if (!$channel) {
  10723. $channel = $this->getDefaultChannel();
  10724. } elseif ($channel != 'pear.php.net') {
  10725. $this->_lazyChannelSetup();
  10726. }
  10727. $channel = strtolower($channel);
  10728. $test = (in_array($key, $this->_channelConfigInfo)) ?
  10729. $this->_getChannelValue($key, $layer, $channel) :
  10730. null;
  10731. if ($test !== null) {
  10732. if ($this->_installRoot) {
  10733. if (in_array($this->getGroup($key),
  10734. array('File Locations', 'File Locations (Advanced)')) &&
  10735. $this->getType($key) == 'directory') {
  10736. return $this->_prependPath($test, $this->_installRoot);
  10737. }
  10738. }
  10739. return $test;
  10740. }
  10741. if ($layer === null) {
  10742. foreach ($this->layers as $layer) {
  10743. if (isset($this->configuration[$layer][$key])) {
  10744. $test = $this->configuration[$layer][$key];
  10745. if ($this->_installRoot) {
  10746. if (in_array($this->getGroup($key),
  10747. array('File Locations', 'File Locations (Advanced)')) &&
  10748. $this->getType($key) == 'directory') {
  10749. return $this->_prependPath($test, $this->_installRoot);
  10750. }
  10751. }
  10752. if ($key == 'preferred_mirror') {
  10753. $reg = &$this->getRegistry();
  10754. if (is_object($reg)) {
  10755. $chan = $reg->getChannel($channel);
  10756. if (PEAR::isError($chan)) {
  10757. return $channel;
  10758. }
  10759. if (!$chan->getMirror($test) && $chan->getName() != $test) {
  10760. return $channel; // mirror does not exist
  10761. }
  10762. }
  10763. }
  10764. return $test;
  10765. }
  10766. }
  10767. } elseif (isset($this->configuration[$layer][$key])) {
  10768. $test = $this->configuration[$layer][$key];
  10769. if ($this->_installRoot) {
  10770. if (in_array($this->getGroup($key),
  10771. array('File Locations', 'File Locations (Advanced)')) &&
  10772. $this->getType($key) == 'directory') {
  10773. return $this->_prependPath($test, $this->_installRoot);
  10774. }
  10775. }
  10776. if ($key == 'preferred_mirror') {
  10777. $reg = &$this->getRegistry();
  10778. if (is_object($reg)) {
  10779. $chan = $reg->getChannel($channel);
  10780. if (PEAR::isError($chan)) {
  10781. return $channel;
  10782. }
  10783. if (!$chan->getMirror($test) && $chan->getName() != $test) {
  10784. return $channel; // mirror does not exist
  10785. }
  10786. }
  10787. }
  10788. return $test;
  10789. }
  10790. return null;
  10791. }
  10792. /**
  10793. * Returns a channel-specific configuration value, prioritizing layers as per the
  10794. * layers property.
  10795. *
  10796. * @param string config key
  10797. * @return mixed the config value, or NULL if not found
  10798. * @access private
  10799. */
  10800. function _getChannelValue($key, $layer, $channel)
  10801. {
  10802. if ($key == '__channels' || $channel == 'pear.php.net') {
  10803. return null;
  10804. }
  10805. $ret = null;
  10806. if ($layer === null) {
  10807. foreach ($this->layers as $ilayer) {
  10808. if (isset($this->configuration[$ilayer]['__channels'][$channel][$key])) {
  10809. $ret = $this->configuration[$ilayer]['__channels'][$channel][$key];
  10810. break;
  10811. }
  10812. }
  10813. } elseif (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
  10814. $ret = $this->configuration[$layer]['__channels'][$channel][$key];
  10815. }
  10816. if ($key != 'preferred_mirror') {
  10817. return $ret;
  10818. }
  10819. if ($ret !== null) {
  10820. $reg = &$this->getRegistry($layer);
  10821. if (is_object($reg)) {
  10822. $chan = $reg->getChannel($channel);
  10823. if (PEAR::isError($chan)) {
  10824. return $channel;
  10825. }
  10826. if (!$chan->getMirror($ret) && $chan->getName() != $ret) {
  10827. return $channel; // mirror does not exist
  10828. }
  10829. }
  10830. return $ret;
  10831. }
  10832. if ($channel != $this->getDefaultChannel($layer)) {
  10833. return $channel; // we must use the channel name as the preferred mirror
  10834. // if the user has not chosen an alternate
  10835. }
  10836. return $this->getDefaultChannel($layer);
  10837. }
  10838. /**
  10839. * Set a config value in a specific layer (defaults to 'user').
  10840. * Enforces the types defined in the configuration_info array. An
  10841. * integer config variable will be cast to int, and a set config
  10842. * variable will be validated against its legal values.
  10843. *
  10844. * @param string config key
  10845. * @param string config value
  10846. * @param string (optional) config layer
  10847. * @param string channel to set this value for, or null for global value
  10848. * @return bool TRUE on success, FALSE on failure
  10849. */
  10850. function set($key, $value, $layer = 'user', $channel = false)
  10851. {
  10852. if ($key == '__channels') {
  10853. return false;
  10854. }
  10855. if (!isset($this->configuration[$layer])) {
  10856. return false;
  10857. }
  10858. if ($key == 'default_channel') {
  10859. // can only set this value globally
  10860. $channel = 'pear.php.net';
  10861. if ($value != 'pear.php.net') {
  10862. $this->_lazyChannelSetup($layer);
  10863. }
  10864. }
  10865. if ($key == 'preferred_mirror') {
  10866. if ($channel == '__uri') {
  10867. return false; // can't set the __uri pseudo-channel's mirror
  10868. }
  10869. $reg = &$this->getRegistry($layer);
  10870. if (is_object($reg)) {
  10871. $chan = $reg->getChannel($channel ? $channel : 'pear.php.net');
  10872. if (PEAR::isError($chan)) {
  10873. return false;
  10874. }
  10875. if (!$chan->getMirror($value) && $chan->getName() != $value) {
  10876. return false; // mirror does not exist
  10877. }
  10878. }
  10879. }
  10880. if (!isset($this->configuration_info[$key])) {
  10881. return false;
  10882. }
  10883. extract($this->configuration_info[$key]);
  10884. switch ($type) {
  10885. case 'integer':
  10886. $value = (int)$value;
  10887. break;
  10888. case 'set': {
  10889. // If a valid_set is specified, require the value to
  10890. // be in the set. If there is no valid_set, accept
  10891. // any value.
  10892. if ($valid_set) {
  10893. reset($valid_set);
  10894. if ((key($valid_set) === 0 && !in_array($value, $valid_set)) ||
  10895. (key($valid_set) !== 0 && empty($valid_set[$value])))
  10896. {
  10897. return false;
  10898. }
  10899. }
  10900. break;
  10901. }
  10902. }
  10903. if (!$channel) {
  10904. $channel = $this->get('default_channel', null, 'pear.php.net');
  10905. }
  10906. if (!in_array($channel, $this->_channels)) {
  10907. $this->_lazyChannelSetup($layer);
  10908. $reg = &$this->getRegistry($layer);
  10909. if ($reg) {
  10910. $channel = $reg->channelName($channel);
  10911. }
  10912. if (!in_array($channel, $this->_channels)) {
  10913. return false;
  10914. }
  10915. }
  10916. if ($channel != 'pear.php.net') {
  10917. if (in_array($key, $this->_channelConfigInfo)) {
  10918. $this->configuration[$layer]['__channels'][$channel][$key] = $value;
  10919. return true;
  10920. }
  10921. return false;
  10922. }
  10923. if ($key == 'default_channel') {
  10924. if (!isset($reg)) {
  10925. $reg = &$this->getRegistry($layer);
  10926. if (!$reg) {
  10927. $reg = &$this->getRegistry();
  10928. }
  10929. }
  10930. if ($reg) {
  10931. $value = $reg->channelName($value);
  10932. }
  10933. if (!$value) {
  10934. return false;
  10935. }
  10936. }
  10937. $this->configuration[$layer][$key] = $value;
  10938. if ($key == 'php_dir' && !$this->_noRegistry) {
  10939. if (!isset($this->_registry[$layer]) ||
  10940. $value != $this->_registry[$layer]->install_dir) {
  10941. $this->_registry[$layer] = new PEAR_Registry($value);
  10942. $this->_regInitialized[$layer] = false;
  10943. $this->_registry[$layer]->setConfig($this, false);
  10944. }
  10945. }
  10946. return true;
  10947. }
  10948. function _lazyChannelSetup($uselayer = false)
  10949. {
  10950. if ($this->_noRegistry) {
  10951. return;
  10952. }
  10953. $merge = false;
  10954. foreach ($this->_registry as $layer => $p) {
  10955. if ($uselayer && $uselayer != $layer) {
  10956. continue;
  10957. }
  10958. if (!$this->_regInitialized[$layer]) {
  10959. if ($layer == 'default' && isset($this->_registry['user']) ||
  10960. isset($this->_registry['system'])) {
  10961. // only use the default registry if there are no alternatives
  10962. continue;
  10963. }
  10964. if (!is_object($this->_registry[$layer])) {
  10965. if ($phpdir = $this->get('php_dir', $layer, 'pear.php.net')) {
  10966. $this->_registry[$layer] = new PEAR_Registry(
  10967. $phpdir, false, false,
  10968. $this->get('metadata_dir', $layer, 'pear.php.net'));
  10969. $this->_registry[$layer]->setConfig($this, false);
  10970. $this->_regInitialized[$layer] = false;
  10971. } else {
  10972. unset($this->_registry[$layer]);
  10973. return;
  10974. }
  10975. }
  10976. $this->setChannels($this->_registry[$layer]->listChannels(), $merge);
  10977. $this->_regInitialized[$layer] = true;
  10978. $merge = true;
  10979. }
  10980. }
  10981. }
  10982. /**
  10983. * Set the list of channels.
  10984. *
  10985. * This should be set via a call to {@link PEAR_Registry::listChannels()}
  10986. * @param array
  10987. * @param bool
  10988. * @return bool success of operation
  10989. */
  10990. function setChannels($channels, $merge = false)
  10991. {
  10992. if (!is_array($channels)) {
  10993. return false;
  10994. }
  10995. if ($merge) {
  10996. $this->_channels = array_merge($this->_channels, $channels);
  10997. } else {
  10998. $this->_channels = $channels;
  10999. }
  11000. foreach ($channels as $channel) {
  11001. $channel = strtolower($channel);
  11002. if ($channel == 'pear.php.net') {
  11003. continue;
  11004. }
  11005. foreach ($this->layers as $layer) {
  11006. if (!isset($this->configuration[$layer]['__channels'])) {
  11007. $this->configuration[$layer]['__channels'] = array();
  11008. }
  11009. if (!isset($this->configuration[$layer]['__channels'][$channel])
  11010. || !is_array($this->configuration[$layer]['__channels'][$channel])) {
  11011. $this->configuration[$layer]['__channels'][$channel] = array();
  11012. }
  11013. }
  11014. }
  11015. return true;
  11016. }
  11017. /**
  11018. * Get the type of a config value.
  11019. *
  11020. * @param string config key
  11021. *
  11022. * @return string type, one of "string", "integer", "file",
  11023. * "directory", "set" or "password".
  11024. *
  11025. * @access public
  11026. *
  11027. */
  11028. function getType($key)
  11029. {
  11030. if (isset($this->configuration_info[$key])) {
  11031. return $this->configuration_info[$key]['type'];
  11032. }
  11033. return false;
  11034. }
  11035. /**
  11036. * Get the documentation for a config value.
  11037. *
  11038. * @param string config key
  11039. * @return string documentation string
  11040. *
  11041. * @access public
  11042. *
  11043. */
  11044. function getDocs($key)
  11045. {
  11046. if (isset($this->configuration_info[$key])) {
  11047. return $this->configuration_info[$key]['doc'];
  11048. }
  11049. return false;
  11050. }
  11051. /**
  11052. * Get the short documentation for a config value.
  11053. *
  11054. * @param string config key
  11055. * @return string short documentation string
  11056. *
  11057. * @access public
  11058. *
  11059. */
  11060. function getPrompt($key)
  11061. {
  11062. if (isset($this->configuration_info[$key])) {
  11063. return $this->configuration_info[$key]['prompt'];
  11064. }
  11065. return false;
  11066. }
  11067. /**
  11068. * Get the parameter group for a config key.
  11069. *
  11070. * @param string config key
  11071. * @return string parameter group
  11072. *
  11073. * @access public
  11074. *
  11075. */
  11076. function getGroup($key)
  11077. {
  11078. if (isset($this->configuration_info[$key])) {
  11079. return $this->configuration_info[$key]['group'];
  11080. }
  11081. return false;
  11082. }
  11083. /**
  11084. * Get the list of parameter groups.
  11085. *
  11086. * @return array list of parameter groups
  11087. *
  11088. * @access public
  11089. *
  11090. */
  11091. function getGroups()
  11092. {
  11093. $tmp = array();
  11094. foreach ($this->configuration_info as $key => $info) {
  11095. $tmp[$info['group']] = 1;
  11096. }
  11097. return array_keys($tmp);
  11098. }
  11099. /**
  11100. * Get the list of the parameters in a group.
  11101. *
  11102. * @param string $group parameter group
  11103. * @return array list of parameters in $group
  11104. *
  11105. * @access public
  11106. *
  11107. */
  11108. function getGroupKeys($group)
  11109. {
  11110. $keys = array();
  11111. foreach ($this->configuration_info as $key => $info) {
  11112. if ($info['group'] == $group) {
  11113. $keys[] = $key;
  11114. }
  11115. }
  11116. return $keys;
  11117. }
  11118. /**
  11119. * Get the list of allowed set values for a config value. Returns
  11120. * NULL for config values that are not sets.
  11121. *
  11122. * @param string config key
  11123. * @return array enumerated array of set values, or NULL if the
  11124. * config key is unknown or not a set
  11125. *
  11126. * @access public
  11127. *
  11128. */
  11129. function getSetValues($key)
  11130. {
  11131. if (isset($this->configuration_info[$key]) &&
  11132. isset($this->configuration_info[$key]['type']) &&
  11133. $this->configuration_info[$key]['type'] == 'set')
  11134. {
  11135. $valid_set = $this->configuration_info[$key]['valid_set'];
  11136. reset($valid_set);
  11137. if (key($valid_set) === 0) {
  11138. return $valid_set;
  11139. }
  11140. return array_keys($valid_set);
  11141. }
  11142. return null;
  11143. }
  11144. /**
  11145. * Get all the current config keys.
  11146. *
  11147. * @return array simple array of config keys
  11148. *
  11149. * @access public
  11150. */
  11151. function getKeys()
  11152. {
  11153. $keys = array();
  11154. foreach ($this->layers as $layer) {
  11155. $test = $this->configuration[$layer];
  11156. if (isset($test['__channels'])) {
  11157. foreach ($test['__channels'] as $channel => $configs) {
  11158. $keys = array_merge($keys, $configs);
  11159. }
  11160. }
  11161. unset($test['__channels']);
  11162. $keys = array_merge($keys, $test);
  11163. }
  11164. return array_keys($keys);
  11165. }
  11166. /**
  11167. * Remove the a config key from a specific config layer.
  11168. *
  11169. * @param string config key
  11170. * @param string (optional) config layer
  11171. * @param string (optional) channel (defaults to default channel)
  11172. * @return bool TRUE on success, FALSE on failure
  11173. *
  11174. * @access public
  11175. */
  11176. function remove($key, $layer = 'user', $channel = null)
  11177. {
  11178. if ($channel === null) {
  11179. $channel = $this->getDefaultChannel();
  11180. }
  11181. if ($channel !== 'pear.php.net') {
  11182. if (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
  11183. unset($this->configuration[$layer]['__channels'][$channel][$key]);
  11184. return true;
  11185. }
  11186. }
  11187. if (isset($this->configuration[$layer][$key])) {
  11188. unset($this->configuration[$layer][$key]);
  11189. return true;
  11190. }
  11191. return false;
  11192. }
  11193. /**
  11194. * Temporarily remove an entire config layer. USE WITH CARE!
  11195. *
  11196. * @param string config key
  11197. * @param string (optional) config layer
  11198. * @return bool TRUE on success, FALSE on failure
  11199. *
  11200. * @access public
  11201. */
  11202. function removeLayer($layer)
  11203. {
  11204. if (isset($this->configuration[$layer])) {
  11205. $this->configuration[$layer] = array();
  11206. return true;
  11207. }
  11208. return false;
  11209. }
  11210. /**
  11211. * Stores configuration data in a layer.
  11212. *
  11213. * @param string config layer to store
  11214. * @return bool TRUE on success, or PEAR error on failure
  11215. *
  11216. * @access public
  11217. */
  11218. function store($layer = 'user', $data = null)
  11219. {
  11220. return $this->writeConfigFile(null, $layer, $data);
  11221. }
  11222. /**
  11223. * Tells what config layer that gets to define a key.
  11224. *
  11225. * @param string config key
  11226. * @param boolean return the defining channel
  11227. *
  11228. * @return string|array the config layer, or an empty string if not found.
  11229. *
  11230. * if $returnchannel, the return is an array array('layer' => layername,
  11231. * 'channel' => channelname), or an empty string if not found
  11232. *
  11233. * @access public
  11234. */
  11235. function definedBy($key, $returnchannel = false)
  11236. {
  11237. foreach ($this->layers as $layer) {
  11238. $channel = $this->getDefaultChannel();
  11239. if ($channel !== 'pear.php.net') {
  11240. if (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
  11241. if ($returnchannel) {
  11242. return array('layer' => $layer, 'channel' => $channel);
  11243. }
  11244. return $layer;
  11245. }
  11246. }
  11247. if (isset($this->configuration[$layer][$key])) {
  11248. if ($returnchannel) {
  11249. return array('layer' => $layer, 'channel' => 'pear.php.net');
  11250. }
  11251. return $layer;
  11252. }
  11253. }
  11254. return '';
  11255. }
  11256. /**
  11257. * Tells whether a given key exists as a config value.
  11258. *
  11259. * @param string config key
  11260. * @return bool whether <config key> exists in this object
  11261. *
  11262. * @access public
  11263. */
  11264. function isDefined($key)
  11265. {
  11266. foreach ($this->layers as $layer) {
  11267. if (isset($this->configuration[$layer][$key])) {
  11268. return true;
  11269. }
  11270. }
  11271. return false;
  11272. }
  11273. /**
  11274. * Tells whether a given config layer exists.
  11275. *
  11276. * @param string config layer
  11277. * @return bool whether <config layer> exists in this object
  11278. *
  11279. * @access public
  11280. */
  11281. function isDefinedLayer($layer)
  11282. {
  11283. return isset($this->configuration[$layer]);
  11284. }
  11285. /**
  11286. * Returns the layers defined (except the 'default' one)
  11287. *
  11288. * @return array of the defined layers
  11289. */
  11290. function getLayers()
  11291. {
  11292. $cf = $this->configuration;
  11293. unset($cf['default']);
  11294. return array_keys($cf);
  11295. }
  11296. function apiVersion()
  11297. {
  11298. return '1.1';
  11299. }
  11300. /**
  11301. * @return PEAR_Registry
  11302. */
  11303. function &getRegistry($use = null)
  11304. {
  11305. $layer = $use === null ? 'user' : $use;
  11306. if (isset($this->_registry[$layer])) {
  11307. return $this->_registry[$layer];
  11308. } elseif ($use === null && isset($this->_registry['system'])) {
  11309. return $this->_registry['system'];
  11310. } elseif ($use === null && isset($this->_registry['default'])) {
  11311. return $this->_registry['default'];
  11312. } elseif ($use) {
  11313. $a = false;
  11314. return $a;
  11315. }
  11316. // only go here if null was passed in
  11317. echo "CRITICAL ERROR: Registry could not be initialized from any value";
  11318. exit(1);
  11319. }
  11320. /**
  11321. * This is to allow customization like the use of installroot
  11322. * @param PEAR_Registry
  11323. * @return bool
  11324. */
  11325. function setRegistry(&$reg, $layer = 'user')
  11326. {
  11327. if ($this->_noRegistry) {
  11328. return false;
  11329. }
  11330. if (!in_array($layer, array('user', 'system'))) {
  11331. return false;
  11332. }
  11333. $this->_registry[$layer] = &$reg;
  11334. if (is_object($reg)) {
  11335. $this->_registry[$layer]->setConfig($this, false);
  11336. }
  11337. return true;
  11338. }
  11339. function noRegistry()
  11340. {
  11341. $this->_noRegistry = true;
  11342. }
  11343. /**
  11344. * @return PEAR_REST
  11345. */
  11346. function &getREST($version, $options = array())
  11347. {
  11348. $version = str_replace('.', '', $version);
  11349. if (!class_exists($class = 'PEAR_REST_' . $version)) {
  11350. require_once 'phar://go-pear.phar/' . 'PEAR/REST/' . $version . '.php';
  11351. }
  11352. $remote = new $class($this, $options);
  11353. return $remote;
  11354. }
  11355. /**
  11356. * The ftp server is set in {@link readFTPConfigFile()}. It exists only if a
  11357. * remote configuration file has been specified
  11358. * @return PEAR_FTP|false
  11359. */
  11360. function &getFTP()
  11361. {
  11362. if (isset($this->_ftp)) {
  11363. return $this->_ftp;
  11364. }
  11365. $a = false;
  11366. return $a;
  11367. }
  11368. static function _prependPath($path, $prepend)
  11369. {
  11370. if (strlen($prepend) > 0) {
  11371. if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
  11372. if (preg_match('/^[a-z]:/i', $prepend)) {
  11373. $prepend = substr($prepend, 2);
  11374. } elseif ($prepend[0] != '\\') {
  11375. $prepend = "\\$prepend";
  11376. }
  11377. $path = substr($path, 0, 2) . $prepend . substr($path, 2);
  11378. } else {
  11379. $path = $prepend . $path;
  11380. }
  11381. }
  11382. return $path;
  11383. }
  11384. /**
  11385. * @param string|false installation directory to prepend to all _dir variables, or false to
  11386. * disable
  11387. */
  11388. function setInstallRoot($root)
  11389. {
  11390. if (substr($root, -1) == DIRECTORY_SEPARATOR) {
  11391. $root = substr($root, 0, -1);
  11392. }
  11393. $old = $this->_installRoot;
  11394. $this->_installRoot = $root;
  11395. if (($old != $root) && !$this->_noRegistry) {
  11396. foreach (array_keys($this->_registry) as $layer) {
  11397. if ($layer == 'ftp' || !isset($this->_registry[$layer])) {
  11398. continue;
  11399. }
  11400. $this->_registry[$layer] =
  11401. new PEAR_Registry(
  11402. $this->get('php_dir', $layer, 'pear.php.net'), false, false,
  11403. $this->get('metadata_dir', $layer, 'pear.php.net'));
  11404. $this->_registry[$layer]->setConfig($this, false);
  11405. $this->_regInitialized[$layer] = false;
  11406. }
  11407. }
  11408. }
  11409. }
  11410. <?php
  11411. /**
  11412. * PEAR_Dependency2, advanced dependency validation
  11413. *
  11414. * PHP versions 4 and 5
  11415. *
  11416. * @category pear
  11417. * @package PEAR
  11418. * @author Greg Beaver <cellog@php.net>
  11419. * @copyright 1997-2009 The Authors
  11420. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  11421. * @link http://pear.php.net/package/PEAR
  11422. * @since File available since Release 1.4.0a1
  11423. */
  11424. /**
  11425. * Required for the PEAR_VALIDATE_* constants
  11426. */
  11427. require_once 'phar://go-pear.phar/' . 'PEAR/Validate.php';
  11428. /**
  11429. * Dependency check for PEAR packages
  11430. *
  11431. * This class handles both version 1.0 and 2.0 dependencies
  11432. * WARNING: *any* changes to this class must be duplicated in the
  11433. * test_PEAR_Dependency2 class found in tests/PEAR_Dependency2/setup.php.inc,
  11434. * or unit tests will not actually validate the changes
  11435. * @category pear
  11436. * @package PEAR
  11437. * @author Greg Beaver <cellog@php.net>
  11438. * @copyright 1997-2009 The Authors
  11439. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  11440. * @version Release: 1.10.16
  11441. * @link http://pear.php.net/package/PEAR
  11442. * @since Class available since Release 1.4.0a1
  11443. */
  11444. class PEAR_Dependency2
  11445. {
  11446. /**
  11447. * One of the PEAR_VALIDATE_* states
  11448. * @see PEAR_VALIDATE_NORMAL
  11449. * @var integer
  11450. */
  11451. var $_state;
  11452. /**
  11453. * Command-line options to install/upgrade/uninstall commands
  11454. * @param array
  11455. */
  11456. var $_options;
  11457. /**
  11458. * @var OS_Guess
  11459. */
  11460. var $_os;
  11461. /**
  11462. * @var PEAR_Registry
  11463. */
  11464. var $_registry;
  11465. /**
  11466. * @var PEAR_Config
  11467. */
  11468. var $_config;
  11469. /**
  11470. * @var PEAR_DependencyDB
  11471. */
  11472. var $_dependencydb;
  11473. /**
  11474. * Output of PEAR_Registry::parsedPackageName()
  11475. * @var array
  11476. */
  11477. var $_currentPackage;
  11478. /**
  11479. * @param PEAR_Config
  11480. * @param array installation options
  11481. * @param array format of PEAR_Registry::parsedPackageName()
  11482. * @param int installation state (one of PEAR_VALIDATE_*)
  11483. */
  11484. function __construct(&$config, $installoptions, $package,
  11485. $state = PEAR_VALIDATE_INSTALLING)
  11486. {
  11487. $this->_config = &$config;
  11488. if (!class_exists('PEAR_DependencyDB')) {
  11489. require_once 'phar://go-pear.phar/' . 'PEAR/DependencyDB.php';
  11490. }
  11491. if (isset($installoptions['packagingroot'])) {
  11492. // make sure depdb is in the right location
  11493. $config->setInstallRoot($installoptions['packagingroot']);
  11494. }
  11495. $this->_registry = &$config->getRegistry();
  11496. $this->_dependencydb = &PEAR_DependencyDB::singleton($config);
  11497. if (isset($installoptions['packagingroot'])) {
  11498. $config->setInstallRoot(false);
  11499. }
  11500. $this->_options = $installoptions;
  11501. $this->_state = $state;
  11502. if (!class_exists('OS_Guess')) {
  11503. require_once 'phar://go-pear.phar/' . 'OS/Guess.php';
  11504. }
  11505. $this->_os = new OS_Guess;
  11506. $this->_currentPackage = $package;
  11507. }
  11508. static function _getExtraString($dep)
  11509. {
  11510. $extra = ' (';
  11511. if (isset($dep['uri'])) {
  11512. return '';
  11513. }
  11514. if (isset($dep['recommended'])) {
  11515. $extra .= 'recommended version ' . $dep['recommended'];
  11516. } else {
  11517. if (isset($dep['min'])) {
  11518. $extra .= 'version >= ' . $dep['min'];
  11519. }
  11520. if (isset($dep['max'])) {
  11521. if ($extra != ' (') {
  11522. $extra .= ', ';
  11523. }
  11524. $extra .= 'version <= ' . $dep['max'];
  11525. }
  11526. if (isset($dep['exclude'])) {
  11527. if (!is_array($dep['exclude'])) {
  11528. $dep['exclude'] = array($dep['exclude']);
  11529. }
  11530. if ($extra != ' (') {
  11531. $extra .= ', ';
  11532. }
  11533. $extra .= 'excluded versions: ';
  11534. foreach ($dep['exclude'] as $i => $exclude) {
  11535. if ($i) {
  11536. $extra .= ', ';
  11537. }
  11538. $extra .= $exclude;
  11539. }
  11540. }
  11541. }
  11542. $extra .= ')';
  11543. if ($extra == ' ()') {
  11544. $extra = '';
  11545. }
  11546. return $extra;
  11547. }
  11548. /**
  11549. * This makes unit-testing a heck of a lot easier
  11550. */
  11551. function getPHP_OS()
  11552. {
  11553. return PHP_OS;
  11554. }
  11555. /**
  11556. * This makes unit-testing a heck of a lot easier
  11557. */
  11558. function getsysname()
  11559. {
  11560. return $this->_os->getSysname();
  11561. }
  11562. /**
  11563. * Specify a dependency on an OS. Use arch for detailed os/processor information
  11564. *
  11565. * There are two generic OS dependencies that will be the most common, unix and windows.
  11566. * Other options are linux, freebsd, darwin (OS X), sunos, irix, hpux, aix
  11567. */
  11568. function validateOsDependency($dep)
  11569. {
  11570. if ($this->_state != PEAR_VALIDATE_INSTALLING && $this->_state != PEAR_VALIDATE_DOWNLOADING) {
  11571. return true;
  11572. }
  11573. if ($dep['name'] == '*') {
  11574. return true;
  11575. }
  11576. $not = isset($dep['conflicts']) ? true : false;
  11577. switch (strtolower($dep['name'])) {
  11578. case 'windows' :
  11579. if ($not) {
  11580. if (strtolower(substr($this->getPHP_OS(), 0, 3)) == 'win') {
  11581. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11582. return $this->raiseError("Cannot install %s on Windows");
  11583. }
  11584. return $this->warning("warning: Cannot install %s on Windows");
  11585. }
  11586. } else {
  11587. if (strtolower(substr($this->getPHP_OS(), 0, 3)) != 'win') {
  11588. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11589. return $this->raiseError("Can only install %s on Windows");
  11590. }
  11591. return $this->warning("warning: Can only install %s on Windows");
  11592. }
  11593. }
  11594. break;
  11595. case 'unix' :
  11596. $unices = array('linux', 'freebsd', 'darwin', 'sunos', 'irix', 'hpux', 'aix');
  11597. if ($not) {
  11598. if (in_array($this->getSysname(), $unices)) {
  11599. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11600. return $this->raiseError("Cannot install %s on any Unix system");
  11601. }
  11602. return $this->warning( "warning: Cannot install %s on any Unix system");
  11603. }
  11604. } else {
  11605. if (!in_array($this->getSysname(), $unices)) {
  11606. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11607. return $this->raiseError("Can only install %s on a Unix system");
  11608. }
  11609. return $this->warning("warning: Can only install %s on a Unix system");
  11610. }
  11611. }
  11612. break;
  11613. default :
  11614. if ($not) {
  11615. if (strtolower($dep['name']) == strtolower($this->getSysname())) {
  11616. if (!isset($this->_options['nodeps']) &&
  11617. !isset($this->_options['force'])) {
  11618. return $this->raiseError('Cannot install %s on ' . $dep['name'] .
  11619. ' operating system');
  11620. }
  11621. return $this->warning('warning: Cannot install %s on ' .
  11622. $dep['name'] . ' operating system');
  11623. }
  11624. } else {
  11625. if (strtolower($dep['name']) != strtolower($this->getSysname())) {
  11626. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11627. return $this->raiseError('Cannot install %s on ' .
  11628. $this->getSysname() .
  11629. ' operating system, can only install on ' . $dep['name']);
  11630. }
  11631. return $this->warning('warning: Cannot install %s on ' .
  11632. $this->getSysname() .
  11633. ' operating system, can only install on ' . $dep['name']);
  11634. }
  11635. }
  11636. }
  11637. return true;
  11638. }
  11639. /**
  11640. * This makes unit-testing a heck of a lot easier
  11641. */
  11642. function matchSignature($pattern)
  11643. {
  11644. return $this->_os->matchSignature($pattern);
  11645. }
  11646. /**
  11647. * Specify a complex dependency on an OS/processor/kernel version,
  11648. * Use OS for simple operating system dependency.
  11649. *
  11650. * This is the only dependency that accepts an eregable pattern. The pattern
  11651. * will be matched against the php_uname() output parsed by OS_Guess
  11652. */
  11653. function validateArchDependency($dep)
  11654. {
  11655. if ($this->_state != PEAR_VALIDATE_INSTALLING) {
  11656. return true;
  11657. }
  11658. $not = isset($dep['conflicts']) ? true : false;
  11659. if (!$this->matchSignature($dep['pattern'])) {
  11660. if (!$not) {
  11661. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11662. return $this->raiseError('%s Architecture dependency failed, does not ' .
  11663. 'match "' . $dep['pattern'] . '"');
  11664. }
  11665. return $this->warning('warning: %s Architecture dependency failed, does ' .
  11666. 'not match "' . $dep['pattern'] . '"');
  11667. }
  11668. return true;
  11669. }
  11670. if ($not) {
  11671. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11672. return $this->raiseError('%s Architecture dependency failed, required "' .
  11673. $dep['pattern'] . '"');
  11674. }
  11675. return $this->warning('warning: %s Architecture dependency failed, ' .
  11676. 'required "' . $dep['pattern'] . '"');
  11677. }
  11678. return true;
  11679. }
  11680. /**
  11681. * This makes unit-testing a heck of a lot easier
  11682. */
  11683. function extension_loaded($name)
  11684. {
  11685. return extension_loaded($name);
  11686. }
  11687. /**
  11688. * This makes unit-testing a heck of a lot easier
  11689. */
  11690. function phpversion($name = null)
  11691. {
  11692. if ($name !== null) {
  11693. return phpversion($name);
  11694. }
  11695. return phpversion();
  11696. }
  11697. function validateExtensionDependency($dep, $required = true)
  11698. {
  11699. if ($this->_state != PEAR_VALIDATE_INSTALLING &&
  11700. $this->_state != PEAR_VALIDATE_DOWNLOADING) {
  11701. return true;
  11702. }
  11703. $loaded = $this->extension_loaded($dep['name']);
  11704. $extra = self::_getExtraString($dep);
  11705. if (isset($dep['exclude'])) {
  11706. if (!is_array($dep['exclude'])) {
  11707. $dep['exclude'] = array($dep['exclude']);
  11708. }
  11709. }
  11710. if (!isset($dep['min']) && !isset($dep['max']) &&
  11711. !isset($dep['recommended']) && !isset($dep['exclude'])
  11712. ) {
  11713. if ($loaded) {
  11714. if (isset($dep['conflicts'])) {
  11715. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11716. return $this->raiseError('%s conflicts with PHP extension "' .
  11717. $dep['name'] . '"' . $extra);
  11718. }
  11719. return $this->warning('warning: %s conflicts with PHP extension "' .
  11720. $dep['name'] . '"' . $extra);
  11721. }
  11722. return true;
  11723. }
  11724. if (isset($dep['conflicts'])) {
  11725. return true;
  11726. }
  11727. if ($required) {
  11728. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11729. return $this->raiseError('%s requires PHP extension "' .
  11730. $dep['name'] . '"' . $extra);
  11731. }
  11732. return $this->warning('warning: %s requires PHP extension "' .
  11733. $dep['name'] . '"' . $extra);
  11734. }
  11735. return $this->warning('%s can optionally use PHP extension "' .
  11736. $dep['name'] . '"' . $extra);
  11737. }
  11738. if (!$loaded) {
  11739. if (isset($dep['conflicts'])) {
  11740. return true;
  11741. }
  11742. if (!$required) {
  11743. return $this->warning('%s can optionally use PHP extension "' .
  11744. $dep['name'] . '"' . $extra);
  11745. }
  11746. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11747. return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
  11748. '"' . $extra);
  11749. }
  11750. return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
  11751. '"' . $extra);
  11752. }
  11753. $version = (string) $this->phpversion($dep['name']);
  11754. if (empty($version)) {
  11755. $version = '0';
  11756. }
  11757. $fail = false;
  11758. if (isset($dep['min']) && !version_compare($version, $dep['min'], '>=')) {
  11759. $fail = true;
  11760. }
  11761. if (isset($dep['max']) && !version_compare($version, $dep['max'], '<=')) {
  11762. $fail = true;
  11763. }
  11764. if ($fail && !isset($dep['conflicts'])) {
  11765. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11766. return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
  11767. '"' . $extra . ', installed version is ' . $version);
  11768. }
  11769. return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
  11770. '"' . $extra . ', installed version is ' . $version);
  11771. } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail && isset($dep['conflicts'])) {
  11772. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11773. return $this->raiseError('%s conflicts with PHP extension "' .
  11774. $dep['name'] . '"' . $extra . ', installed version is ' . $version);
  11775. }
  11776. return $this->warning('warning: %s conflicts with PHP extension "' .
  11777. $dep['name'] . '"' . $extra . ', installed version is ' . $version);
  11778. }
  11779. if (isset($dep['exclude'])) {
  11780. foreach ($dep['exclude'] as $exclude) {
  11781. if (version_compare($version, $exclude, '==')) {
  11782. if (isset($dep['conflicts'])) {
  11783. continue;
  11784. }
  11785. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11786. return $this->raiseError('%s is not compatible with PHP extension "' .
  11787. $dep['name'] . '" version ' .
  11788. $exclude);
  11789. }
  11790. return $this->warning('warning: %s is not compatible with PHP extension "' .
  11791. $dep['name'] . '" version ' .
  11792. $exclude);
  11793. } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
  11794. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11795. return $this->raiseError('%s conflicts with PHP extension "' .
  11796. $dep['name'] . '"' . $extra . ', installed version is ' . $version);
  11797. }
  11798. return $this->warning('warning: %s conflicts with PHP extension "' .
  11799. $dep['name'] . '"' . $extra . ', installed version is ' . $version);
  11800. }
  11801. }
  11802. }
  11803. if (isset($dep['recommended'])) {
  11804. if (version_compare($version, $dep['recommended'], '==')) {
  11805. return true;
  11806. }
  11807. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11808. return $this->raiseError('%s dependency: PHP extension ' . $dep['name'] .
  11809. ' version "' . $version . '"' .
  11810. ' is not the recommended version "' . $dep['recommended'] .
  11811. '", but may be compatible, use --force to install');
  11812. }
  11813. return $this->warning('warning: %s dependency: PHP extension ' .
  11814. $dep['name'] . ' version "' . $version . '"' .
  11815. ' is not the recommended version "' . $dep['recommended'].'"');
  11816. }
  11817. return true;
  11818. }
  11819. function validatePhpDependency($dep)
  11820. {
  11821. if ($this->_state != PEAR_VALIDATE_INSTALLING &&
  11822. $this->_state != PEAR_VALIDATE_DOWNLOADING) {
  11823. return true;
  11824. }
  11825. $version = $this->phpversion();
  11826. $extra = self::_getExtraString($dep);
  11827. if (isset($dep['exclude'])) {
  11828. if (!is_array($dep['exclude'])) {
  11829. $dep['exclude'] = array($dep['exclude']);
  11830. }
  11831. }
  11832. if (isset($dep['min'])) {
  11833. if (!version_compare($version, $dep['min'], '>=')) {
  11834. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11835. return $this->raiseError('%s requires PHP' .
  11836. $extra . ', installed version is ' . $version);
  11837. }
  11838. return $this->warning('warning: %s requires PHP' .
  11839. $extra . ', installed version is ' . $version);
  11840. }
  11841. }
  11842. if (isset($dep['max'])) {
  11843. if (!version_compare($version, $dep['max'], '<=')) {
  11844. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11845. return $this->raiseError('%s requires PHP' .
  11846. $extra . ', installed version is ' . $version);
  11847. }
  11848. return $this->warning('warning: %s requires PHP' .
  11849. $extra . ', installed version is ' . $version);
  11850. }
  11851. }
  11852. if (isset($dep['exclude'])) {
  11853. foreach ($dep['exclude'] as $exclude) {
  11854. if (version_compare($version, $exclude, '==')) {
  11855. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11856. return $this->raiseError('%s is not compatible with PHP version ' .
  11857. $exclude);
  11858. }
  11859. return $this->warning(
  11860. 'warning: %s is not compatible with PHP version ' .
  11861. $exclude);
  11862. }
  11863. }
  11864. }
  11865. return true;
  11866. }
  11867. /**
  11868. * This makes unit-testing a heck of a lot easier
  11869. */
  11870. function getPEARVersion()
  11871. {
  11872. return '1.10.16';
  11873. }
  11874. function validatePearinstallerDependency($dep)
  11875. {
  11876. $pearversion = $this->getPEARVersion();
  11877. $extra = self::_getExtraString($dep);
  11878. if (isset($dep['exclude'])) {
  11879. if (!is_array($dep['exclude'])) {
  11880. $dep['exclude'] = array($dep['exclude']);
  11881. }
  11882. }
  11883. if (version_compare($pearversion, $dep['min'], '<')) {
  11884. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11885. return $this->raiseError('%s requires PEAR Installer' . $extra .
  11886. ', installed version is ' . $pearversion);
  11887. }
  11888. return $this->warning('warning: %s requires PEAR Installer' . $extra .
  11889. ', installed version is ' . $pearversion);
  11890. }
  11891. if (isset($dep['max'])) {
  11892. if (version_compare($pearversion, $dep['max'], '>')) {
  11893. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11894. return $this->raiseError('%s requires PEAR Installer' . $extra .
  11895. ', installed version is ' . $pearversion);
  11896. }
  11897. return $this->warning('warning: %s requires PEAR Installer' . $extra .
  11898. ', installed version is ' . $pearversion);
  11899. }
  11900. }
  11901. if (isset($dep['exclude'])) {
  11902. if (!isset($dep['exclude'][0])) {
  11903. $dep['exclude'] = array($dep['exclude']);
  11904. }
  11905. foreach ($dep['exclude'] as $exclude) {
  11906. if (version_compare($exclude, $pearversion, '==')) {
  11907. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  11908. return $this->raiseError('%s is not compatible with PEAR Installer ' .
  11909. 'version ' . $exclude);
  11910. }
  11911. return $this->warning('warning: %s is not compatible with PEAR ' .
  11912. 'Installer version ' . $exclude);
  11913. }
  11914. }
  11915. }
  11916. return true;
  11917. }
  11918. function validateSubpackageDependency($dep, $required, $params)
  11919. {
  11920. return $this->validatePackageDependency($dep, $required, $params);
  11921. }
  11922. /**
  11923. * @param array dependency information (2.0 format)
  11924. * @param boolean whether this is a required dependency
  11925. * @param array a list of downloaded packages to be installed, if any
  11926. * @param boolean if true, then deps on pear.php.net that fail will also check
  11927. * against pecl.php.net packages to accommodate extensions that have
  11928. * moved to pecl.php.net from pear.php.net
  11929. */
  11930. function validatePackageDependency($dep, $required, $params, $depv1 = false)
  11931. {
  11932. if ($this->_state != PEAR_VALIDATE_INSTALLING &&
  11933. $this->_state != PEAR_VALIDATE_DOWNLOADING) {
  11934. return true;
  11935. }
  11936. if (isset($dep['providesextension'])) {
  11937. if ($this->extension_loaded($dep['providesextension'])) {
  11938. $save = $dep;
  11939. $subdep = $dep;
  11940. $subdep['name'] = $subdep['providesextension'];
  11941. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  11942. $ret = $this->validateExtensionDependency($subdep, $required);
  11943. PEAR::popErrorHandling();
  11944. if (!PEAR::isError($ret)) {
  11945. return true;
  11946. }
  11947. }
  11948. }
  11949. if ($this->_state == PEAR_VALIDATE_INSTALLING) {
  11950. return $this->_validatePackageInstall($dep, $required, $depv1);
  11951. }
  11952. if ($this->_state == PEAR_VALIDATE_DOWNLOADING) {
  11953. return $this->_validatePackageDownload($dep, $required, $params, $depv1);
  11954. }
  11955. }
  11956. function _validatePackageDownload($dep, $required, $params, $depv1 = false)
  11957. {
  11958. $dep['package'] = $dep['name'];
  11959. if (isset($dep['uri'])) {
  11960. $dep['channel'] = '__uri';
  11961. }
  11962. $depname = $this->_registry->parsedPackageNameToString($dep, true);
  11963. $found = false;
  11964. foreach ($params as $param) {
  11965. if ($param->isEqual(
  11966. array('package' => $dep['name'],
  11967. 'channel' => $dep['channel']))) {
  11968. $found = true;
  11969. break;
  11970. }
  11971. if ($depv1 && $dep['channel'] == 'pear.php.net') {
  11972. if ($param->isEqual(
  11973. array('package' => $dep['name'],
  11974. 'channel' => 'pecl.php.net'))) {
  11975. $found = true;
  11976. break;
  11977. }
  11978. }
  11979. }
  11980. if (!$found && isset($dep['providesextension'])) {
  11981. foreach ($params as $param) {
  11982. if ($param->isExtension($dep['providesextension'])) {
  11983. $found = true;
  11984. break;
  11985. }
  11986. }
  11987. }
  11988. if ($found) {
  11989. $version = $param->getVersion();
  11990. $installed = false;
  11991. $downloaded = true;
  11992. } else {
  11993. if ($this->_registry->packageExists($dep['name'], $dep['channel'])) {
  11994. $installed = true;
  11995. $downloaded = false;
  11996. $version = $this->_registry->packageinfo($dep['name'], 'version',
  11997. $dep['channel']);
  11998. } else {
  11999. if ($dep['channel'] == 'pecl.php.net' && $this->_registry->packageExists($dep['name'],
  12000. 'pear.php.net')) {
  12001. $installed = true;
  12002. $downloaded = false;
  12003. $version = $this->_registry->packageinfo($dep['name'], 'version',
  12004. 'pear.php.net');
  12005. } else {
  12006. $version = 'not installed or downloaded';
  12007. $installed = false;
  12008. $downloaded = false;
  12009. }
  12010. }
  12011. }
  12012. $extra = self::_getExtraString($dep);
  12013. if (isset($dep['exclude']) && !is_array($dep['exclude'])) {
  12014. $dep['exclude'] = array($dep['exclude']);
  12015. }
  12016. if (!isset($dep['min']) && !isset($dep['max']) &&
  12017. !isset($dep['recommended']) && !isset($dep['exclude'])
  12018. ) {
  12019. if ($installed || $downloaded) {
  12020. $installed = $installed ? 'installed' : 'downloaded';
  12021. if (isset($dep['conflicts'])) {
  12022. $rest = '';
  12023. if ($version) {
  12024. $rest = ", $installed version is " . $version;
  12025. }
  12026. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  12027. return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra . $rest);
  12028. }
  12029. return $this->warning('warning: %s conflicts with package "' . $depname . '"' . $extra . $rest);
  12030. }
  12031. return true;
  12032. }
  12033. if (isset($dep['conflicts'])) {
  12034. return true;
  12035. }
  12036. if ($required) {
  12037. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  12038. return $this->raiseError('%s requires package "' . $depname . '"' . $extra);
  12039. }
  12040. return $this->warning('warning: %s requires package "' . $depname . '"' . $extra);
  12041. }
  12042. return $this->warning('%s can optionally use package "' . $depname . '"' . $extra);
  12043. }
  12044. if (!$installed && !$downloaded) {
  12045. if (isset($dep['conflicts'])) {
  12046. return true;
  12047. }
  12048. if ($required) {
  12049. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  12050. return $this->raiseError('%s requires package "' . $depname . '"' . $extra);
  12051. }
  12052. return $this->warning('warning: %s requires package "' . $depname . '"' . $extra);
  12053. }
  12054. return $this->warning('%s can optionally use package "' . $depname . '"' . $extra);
  12055. }
  12056. $fail = false;
  12057. if (isset($dep['min']) && version_compare($version, $dep['min'], '<')) {
  12058. $fail = true;
  12059. }
  12060. if (isset($dep['max']) && version_compare($version, $dep['max'], '>')) {
  12061. $fail = true;
  12062. }
  12063. if ($fail && !isset($dep['conflicts'])) {
  12064. $installed = $installed ? 'installed' : 'downloaded';
  12065. $dep['package'] = $dep['name'];
  12066. $dep = $this->_registry->parsedPackageNameToString($dep, true);
  12067. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  12068. return $this->raiseError('%s requires package "' . $depname . '"' .
  12069. $extra . ", $installed version is " . $version);
  12070. }
  12071. return $this->warning('warning: %s requires package "' . $depname . '"' .
  12072. $extra . ", $installed version is " . $version);
  12073. } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail &&
  12074. isset($dep['conflicts']) && !isset($dep['exclude'])) {
  12075. $installed = $installed ? 'installed' : 'downloaded';
  12076. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  12077. return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra .
  12078. ", $installed version is " . $version);
  12079. }
  12080. return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
  12081. $extra . ", $installed version is " . $version);
  12082. }
  12083. if (isset($dep['exclude'])) {
  12084. $installed = $installed ? 'installed' : 'downloaded';
  12085. foreach ($dep['exclude'] as $exclude) {
  12086. if (version_compare($version, $exclude, '==') && !isset($dep['conflicts'])) {
  12087. if (!isset($this->_options['nodeps']) &&
  12088. !isset($this->_options['force'])
  12089. ) {
  12090. return $this->raiseError('%s is not compatible with ' .
  12091. $installed . ' package "' .
  12092. $depname . '" version ' .
  12093. $exclude);
  12094. }
  12095. return $this->warning('warning: %s is not compatible with ' .
  12096. $installed . ' package "' .
  12097. $depname . '" version ' .
  12098. $exclude);
  12099. } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
  12100. $installed = $installed ? 'installed' : 'downloaded';
  12101. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  12102. return $this->raiseError('%s conflicts with package "' . $depname . '"' .
  12103. $extra . ", $installed version is " . $version);
  12104. }
  12105. return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
  12106. $extra . ", $installed version is " . $version);
  12107. }
  12108. }
  12109. }
  12110. if (isset($dep['recommended'])) {
  12111. $installed = $installed ? 'installed' : 'downloaded';
  12112. if (version_compare($version, $dep['recommended'], '==')) {
  12113. return true;
  12114. }
  12115. if (!$found && $installed) {
  12116. $param = $this->_registry->getPackage($dep['name'], $dep['channel']);
  12117. }
  12118. if ($param) {
  12119. $found = false;
  12120. foreach ($params as $parent) {
  12121. if ($parent->isEqual($this->_currentPackage)) {
  12122. $found = true;
  12123. break;
  12124. }
  12125. }
  12126. if ($found) {
  12127. if ($param->isCompatible($parent)) {
  12128. return true;
  12129. }
  12130. } else { // this is for validPackage() calls
  12131. $parent = $this->_registry->getPackage($this->_currentPackage['package'],
  12132. $this->_currentPackage['channel']);
  12133. if ($parent !== null && $param->isCompatible($parent)) {
  12134. return true;
  12135. }
  12136. }
  12137. }
  12138. if (!isset($this->_options['nodeps']) && !isset($this->_options['force']) &&
  12139. !isset($this->_options['loose'])
  12140. ) {
  12141. return $this->raiseError('%s dependency package "' . $depname .
  12142. '" ' . $installed . ' version ' . $version .
  12143. ' is not the recommended version ' . $dep['recommended'] .
  12144. ', but may be compatible, use --force to install');
  12145. }
  12146. return $this->warning('warning: %s dependency package "' . $depname .
  12147. '" ' . $installed . ' version ' . $version .
  12148. ' is not the recommended version ' . $dep['recommended']);
  12149. }
  12150. return true;
  12151. }
  12152. function _validatePackageInstall($dep, $required, $depv1 = false)
  12153. {
  12154. return $this->_validatePackageDownload($dep, $required, array(), $depv1);
  12155. }
  12156. /**
  12157. * Verify that uninstalling packages passed in to command line is OK.
  12158. *
  12159. * @param PEAR_Installer $dl
  12160. * @return PEAR_Error|true
  12161. */
  12162. function validatePackageUninstall(&$dl)
  12163. {
  12164. if (PEAR::isError($this->_dependencydb)) {
  12165. return $this->_dependencydb;
  12166. }
  12167. $params = array();
  12168. // construct an array of "downloaded" packages to fool the package dependency checker
  12169. // into using these to validate uninstalls of circular dependencies
  12170. $downloaded = &$dl->getUninstallPackages();
  12171. foreach ($downloaded as $i => $pf) {
  12172. if (!class_exists('PEAR_Downloader_Package')) {
  12173. require_once 'phar://go-pear.phar/' . 'PEAR/Downloader/Package.php';
  12174. }
  12175. $dp = new PEAR_Downloader_Package($dl);
  12176. $dp->setPackageFile($downloaded[$i]);
  12177. $params[$i] = $dp;
  12178. }
  12179. // check cache
  12180. $memyselfandI = strtolower($this->_currentPackage['channel']) . '/' .
  12181. strtolower($this->_currentPackage['package']);
  12182. if (isset($dl->___uninstall_package_cache)) {
  12183. $badpackages = $dl->___uninstall_package_cache;
  12184. if (isset($badpackages[$memyselfandI]['warnings'])) {
  12185. foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
  12186. $dl->log(0, $warning[0]);
  12187. }
  12188. }
  12189. if (isset($badpackages[$memyselfandI]['errors'])) {
  12190. foreach ($badpackages[$memyselfandI]['errors'] as $error) {
  12191. if (is_array($error)) {
  12192. $dl->log(0, $error[0]);
  12193. } else {
  12194. $dl->log(0, $error->getMessage());
  12195. }
  12196. }
  12197. if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
  12198. return $this->warning(
  12199. 'warning: %s should not be uninstalled, other installed packages depend ' .
  12200. 'on this package');
  12201. }
  12202. return $this->raiseError(
  12203. '%s cannot be uninstalled, other installed packages depend on this package');
  12204. }
  12205. return true;
  12206. }
  12207. // first, list the immediate parents of each package to be uninstalled
  12208. $perpackagelist = array();
  12209. $allparents = array();
  12210. foreach ($params as $i => $param) {
  12211. $a = array(
  12212. 'channel' => strtolower($param->getChannel()),
  12213. 'package' => strtolower($param->getPackage())
  12214. );
  12215. $deps = $this->_dependencydb->getDependentPackages($a);
  12216. if ($deps) {
  12217. foreach ($deps as $d) {
  12218. $pardeps = $this->_dependencydb->getDependencies($d);
  12219. foreach ($pardeps as $dep) {
  12220. if (strtolower($dep['dep']['channel']) == $a['channel'] &&
  12221. strtolower($dep['dep']['name']) == $a['package']) {
  12222. if (!isset($perpackagelist[$a['channel'] . '/' . $a['package']])) {
  12223. $perpackagelist[$a['channel'] . '/' . $a['package']] = array();
  12224. }
  12225. $perpackagelist[$a['channel'] . '/' . $a['package']][]
  12226. = array($d['channel'] . '/' . $d['package'], $dep);
  12227. if (!isset($allparents[$d['channel'] . '/' . $d['package']])) {
  12228. $allparents[$d['channel'] . '/' . $d['package']] = array();
  12229. }
  12230. if (!isset($allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']])) {
  12231. $allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']] = array();
  12232. }
  12233. $allparents[$d['channel'] . '/' . $d['package']]
  12234. [$a['channel'] . '/' . $a['package']][]
  12235. = array($d, $dep);
  12236. }
  12237. }
  12238. }
  12239. }
  12240. }
  12241. // next, remove any packages from the parents list that are not installed
  12242. $remove = array();
  12243. foreach ($allparents as $parent => $d1) {
  12244. foreach ($d1 as $d) {
  12245. if ($this->_registry->packageExists($d[0][0]['package'], $d[0][0]['channel'])) {
  12246. continue;
  12247. }
  12248. $remove[$parent] = true;
  12249. }
  12250. }
  12251. // next remove any packages from the parents list that are not passed in for
  12252. // uninstallation
  12253. foreach ($allparents as $parent => $d1) {
  12254. foreach ($d1 as $d) {
  12255. foreach ($params as $param) {
  12256. if (strtolower($param->getChannel()) == $d[0][0]['channel'] &&
  12257. strtolower($param->getPackage()) == $d[0][0]['package']) {
  12258. // found it
  12259. continue 3;
  12260. }
  12261. }
  12262. $remove[$parent] = true;
  12263. }
  12264. }
  12265. // remove all packages whose dependencies fail
  12266. // save which ones failed for error reporting
  12267. $badchildren = array();
  12268. do {
  12269. $fail = false;
  12270. foreach ($remove as $package => $unused) {
  12271. if (!isset($allparents[$package])) {
  12272. continue;
  12273. }
  12274. foreach ($allparents[$package] as $kid => $d1) {
  12275. foreach ($d1 as $depinfo) {
  12276. if ($depinfo[1]['type'] != 'optional') {
  12277. if (isset($badchildren[$kid])) {
  12278. continue;
  12279. }
  12280. $badchildren[$kid] = true;
  12281. $remove[$kid] = true;
  12282. $fail = true;
  12283. continue 2;
  12284. }
  12285. }
  12286. }
  12287. if ($fail) {
  12288. // start over, we removed some children
  12289. continue 2;
  12290. }
  12291. }
  12292. } while ($fail);
  12293. // next, construct the list of packages that can't be uninstalled
  12294. $badpackages = array();
  12295. $save = $this->_currentPackage;
  12296. foreach ($perpackagelist as $package => $packagedeps) {
  12297. foreach ($packagedeps as $parent) {
  12298. if (!isset($remove[$parent[0]])) {
  12299. continue;
  12300. }
  12301. $packagename = $this->_registry->parsePackageName($parent[0]);
  12302. $packagename['channel'] = $this->_registry->channelAlias($packagename['channel']);
  12303. $pa = $this->_registry->getPackage($packagename['package'], $packagename['channel']);
  12304. $packagename['package'] = $pa->getPackage();
  12305. $this->_currentPackage = $packagename;
  12306. // parent is not present in uninstall list, make sure we can actually
  12307. // uninstall it (parent dep is optional)
  12308. $parentname['channel'] = $this->_registry->channelAlias($parent[1]['dep']['channel']);
  12309. $pa = $this->_registry->getPackage($parent[1]['dep']['name'], $parent[1]['dep']['channel']);
  12310. $parentname['package'] = $pa->getPackage();
  12311. $parent[1]['dep']['package'] = $parentname['package'];
  12312. $parent[1]['dep']['channel'] = $parentname['channel'];
  12313. if ($parent[1]['type'] == 'optional') {
  12314. $test = $this->_validatePackageUninstall($parent[1]['dep'], false, $dl);
  12315. if ($test !== true) {
  12316. $badpackages[$package]['warnings'][] = $test;
  12317. }
  12318. } else {
  12319. $test = $this->_validatePackageUninstall($parent[1]['dep'], true, $dl);
  12320. if ($test !== true) {
  12321. $badpackages[$package]['errors'][] = $test;
  12322. }
  12323. }
  12324. }
  12325. }
  12326. $this->_currentPackage = $save;
  12327. $dl->___uninstall_package_cache = $badpackages;
  12328. if (isset($badpackages[$memyselfandI])) {
  12329. if (isset($badpackages[$memyselfandI]['warnings'])) {
  12330. foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
  12331. $dl->log(0, $warning[0]);
  12332. }
  12333. }
  12334. if (isset($badpackages[$memyselfandI]['errors'])) {
  12335. foreach ($badpackages[$memyselfandI]['errors'] as $error) {
  12336. if (is_array($error)) {
  12337. $dl->log(0, $error[0]);
  12338. } else {
  12339. $dl->log(0, $error->getMessage());
  12340. }
  12341. }
  12342. if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
  12343. return $this->warning(
  12344. 'warning: %s should not be uninstalled, other installed packages depend ' .
  12345. 'on this package');
  12346. }
  12347. return $this->raiseError(
  12348. '%s cannot be uninstalled, other installed packages depend on this package');
  12349. }
  12350. }
  12351. return true;
  12352. }
  12353. function _validatePackageUninstall($dep, $required, $dl)
  12354. {
  12355. $depname = $this->_registry->parsedPackageNameToString($dep, true);
  12356. $version = $this->_registry->packageinfo($dep['package'], 'version', $dep['channel']);
  12357. if (!$version) {
  12358. return true;
  12359. }
  12360. $extra = self::_getExtraString($dep);
  12361. if (isset($dep['exclude']) && !is_array($dep['exclude'])) {
  12362. $dep['exclude'] = array($dep['exclude']);
  12363. }
  12364. if (isset($dep['conflicts'])) {
  12365. return true; // uninstall OK - these packages conflict (probably installed with --force)
  12366. }
  12367. if (!isset($dep['min']) && !isset($dep['max'])) {
  12368. if (!$required) {
  12369. return $this->warning('"' . $depname . '" can be optionally used by ' .
  12370. 'installed package %s' . $extra);
  12371. }
  12372. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  12373. return $this->raiseError('"' . $depname . '" is required by ' .
  12374. 'installed package %s' . $extra);
  12375. }
  12376. return $this->warning('warning: "' . $depname . '" is required by ' .
  12377. 'installed package %s' . $extra);
  12378. }
  12379. $fail = false;
  12380. if (isset($dep['min']) && version_compare($version, $dep['min'], '>=')) {
  12381. $fail = true;
  12382. }
  12383. if (isset($dep['max']) && version_compare($version, $dep['max'], '<=')) {
  12384. $fail = true;
  12385. }
  12386. // we re-use this variable, preserve the original value
  12387. $saverequired = $required;
  12388. if (!$required) {
  12389. return $this->warning($depname . $extra . ' can be optionally used by installed package' .
  12390. ' "%s"');
  12391. }
  12392. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  12393. return $this->raiseError($depname . $extra . ' is required by installed package' .
  12394. ' "%s"');
  12395. }
  12396. return $this->raiseError('warning: ' . $depname . $extra .
  12397. ' is required by installed package "%s"');
  12398. }
  12399. /**
  12400. * validate a downloaded package against installed packages
  12401. *
  12402. * As of PEAR 1.4.3, this will only validate
  12403. *
  12404. * @param array|PEAR_Downloader_Package|PEAR_PackageFile_v1|PEAR_PackageFile_v2
  12405. * $pkg package identifier (either
  12406. * array('package' => blah, 'channel' => blah) or an array with
  12407. * index 'info' referencing an object)
  12408. * @param PEAR_Downloader $dl
  12409. * @param array $params full list of packages to install
  12410. * @return true|PEAR_Error
  12411. */
  12412. function validatePackage($pkg, &$dl, $params = array())
  12413. {
  12414. if (is_array($pkg) && isset($pkg['info'])) {
  12415. $deps = $this->_dependencydb->getDependentPackageDependencies($pkg['info']);
  12416. } else {
  12417. $deps = $this->_dependencydb->getDependentPackageDependencies($pkg);
  12418. }
  12419. $fail = false;
  12420. if ($deps) {
  12421. if (!class_exists('PEAR_Downloader_Package')) {
  12422. require_once 'phar://go-pear.phar/' . 'PEAR/Downloader/Package.php';
  12423. }
  12424. $dp = new PEAR_Downloader_Package($dl);
  12425. if (is_object($pkg)) {
  12426. $dp->setPackageFile($pkg);
  12427. } else {
  12428. $dp->setDownloadURL($pkg);
  12429. }
  12430. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  12431. foreach ($deps as $channel => $info) {
  12432. foreach ($info as $package => $ds) {
  12433. foreach ($params as $packd) {
  12434. if (strtolower($packd->getPackage()) == strtolower($package) &&
  12435. $packd->getChannel() == $channel) {
  12436. $dl->log(3, 'skipping installed package check of "' .
  12437. $this->_registry->parsedPackageNameToString(
  12438. array('channel' => $channel, 'package' => $package),
  12439. true) .
  12440. '", version "' . $packd->getVersion() . '" will be ' .
  12441. 'downloaded and installed');
  12442. continue 2; // jump to next package
  12443. }
  12444. }
  12445. foreach ($ds as $d) {
  12446. $checker = new PEAR_Dependency2($this->_config, $this->_options,
  12447. array('channel' => $channel, 'package' => $package), $this->_state);
  12448. $dep = $d['dep'];
  12449. $required = $d['type'] == 'required';
  12450. $ret = $checker->_validatePackageDownload($dep, $required, array(&$dp));
  12451. if (is_array($ret)) {
  12452. $dl->log(0, $ret[0]);
  12453. } elseif (PEAR::isError($ret)) {
  12454. $dl->log(0, $ret->getMessage());
  12455. $fail = true;
  12456. }
  12457. }
  12458. }
  12459. }
  12460. PEAR::popErrorHandling();
  12461. }
  12462. if ($fail) {
  12463. return $this->raiseError(
  12464. '%s cannot be installed, conflicts with installed packages');
  12465. }
  12466. return true;
  12467. }
  12468. /**
  12469. * validate a package.xml 1.0 dependency
  12470. */
  12471. function validateDependency1($dep, $params = array())
  12472. {
  12473. if (!isset($dep['optional'])) {
  12474. $dep['optional'] = 'no';
  12475. }
  12476. list($newdep, $type) = self::normalizeDep($dep);
  12477. if (!$newdep) {
  12478. return $this->raiseError("Invalid Dependency");
  12479. }
  12480. if (method_exists($this, "validate{$type}Dependency")) {
  12481. return $this->{"validate{$type}Dependency"}($newdep, $dep['optional'] == 'no',
  12482. $params, true);
  12483. }
  12484. }
  12485. /**
  12486. * Convert a 1.0 dep into a 2.0 dep
  12487. */
  12488. static function normalizeDep($dep)
  12489. {
  12490. $types = array(
  12491. 'pkg' => 'Package',
  12492. 'ext' => 'Extension',
  12493. 'os' => 'Os',
  12494. 'php' => 'Php'
  12495. );
  12496. if (!isset($types[$dep['type']])) {
  12497. return array(false, false);
  12498. }
  12499. $type = $types[$dep['type']];
  12500. $newdep = array();
  12501. switch ($type) {
  12502. case 'Package' :
  12503. $newdep['channel'] = 'pear.php.net';
  12504. case 'Extension' :
  12505. case 'Os' :
  12506. $newdep['name'] = $dep['name'];
  12507. break;
  12508. }
  12509. $dep['rel'] = PEAR_Dependency2::signOperator($dep['rel']);
  12510. switch ($dep['rel']) {
  12511. case 'has' :
  12512. return array($newdep, $type);
  12513. break;
  12514. case 'not' :
  12515. $newdep['conflicts'] = true;
  12516. break;
  12517. case '>=' :
  12518. case '>' :
  12519. $newdep['min'] = $dep['version'];
  12520. if ($dep['rel'] == '>') {
  12521. $newdep['exclude'] = $dep['version'];
  12522. }
  12523. break;
  12524. case '<=' :
  12525. case '<' :
  12526. $newdep['max'] = $dep['version'];
  12527. if ($dep['rel'] == '<') {
  12528. $newdep['exclude'] = $dep['version'];
  12529. }
  12530. break;
  12531. case 'ne' :
  12532. case '!=' :
  12533. $newdep['min'] = '0';
  12534. $newdep['max'] = '100000';
  12535. $newdep['exclude'] = $dep['version'];
  12536. break;
  12537. case '==' :
  12538. $newdep['min'] = $dep['version'];
  12539. $newdep['max'] = $dep['version'];
  12540. break;
  12541. }
  12542. if ($type == 'Php') {
  12543. if (!isset($newdep['min'])) {
  12544. $newdep['min'] = '4.4.0';
  12545. }
  12546. if (!isset($newdep['max'])) {
  12547. $newdep['max'] = '6.0.0';
  12548. }
  12549. }
  12550. return array($newdep, $type);
  12551. }
  12552. /**
  12553. * Converts text comparing operators to them sign equivalents
  12554. *
  12555. * Example: 'ge' to '>='
  12556. *
  12557. * @access public
  12558. * @param string Operator
  12559. * @return string Sign equivalent
  12560. */
  12561. static function signOperator($operator)
  12562. {
  12563. switch($operator) {
  12564. case 'lt': return '<';
  12565. case 'le': return '<=';
  12566. case 'gt': return '>';
  12567. case 'ge': return '>=';
  12568. case 'eq': return '==';
  12569. case 'ne': return '!=';
  12570. default:
  12571. return $operator;
  12572. }
  12573. }
  12574. function raiseError($msg)
  12575. {
  12576. if (isset($this->_options['ignore-errors'])) {
  12577. return $this->warning($msg);
  12578. }
  12579. return PEAR::raiseError(sprintf($msg, $this->_registry->parsedPackageNameToString(
  12580. $this->_currentPackage, true)));
  12581. }
  12582. function warning($msg)
  12583. {
  12584. return array(sprintf($msg, $this->_registry->parsedPackageNameToString(
  12585. $this->_currentPackage, true)));
  12586. }
  12587. }
  12588. <?php
  12589. /**
  12590. * PEAR_DependencyDB, advanced installed packages dependency database
  12591. *
  12592. * PHP versions 4 and 5
  12593. *
  12594. * @category pear
  12595. * @package PEAR
  12596. * @author Tomas V. V. Cox <cox@idecnet.com>
  12597. * @author Greg Beaver <cellog@php.net>
  12598. * @copyright 1997-2009 The Authors
  12599. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  12600. * @link http://pear.php.net/package/PEAR
  12601. * @since File available since Release 1.4.0a1
  12602. */
  12603. /**
  12604. * Needed for error handling
  12605. */
  12606. require_once 'phar://go-pear.phar/' . 'PEAR.php';
  12607. require_once 'phar://go-pear.phar/' . 'PEAR/Config.php';
  12608. $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] = array();
  12609. /**
  12610. * Track dependency relationships between installed packages
  12611. * @category pear
  12612. * @package PEAR
  12613. * @author Greg Beaver <cellog@php.net>
  12614. * @author Tomas V.V.Cox <cox@idec.net.com>
  12615. * @copyright 1997-2009 The Authors
  12616. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  12617. * @version Release: 1.10.16
  12618. * @link http://pear.php.net/package/PEAR
  12619. * @since Class available since Release 1.4.0a1
  12620. */
  12621. class PEAR_DependencyDB
  12622. {
  12623. // {{{ properties
  12624. /**
  12625. * This is initialized by {@link setConfig()}
  12626. * @var PEAR_Config
  12627. * @access private
  12628. */
  12629. var $_config;
  12630. /**
  12631. * This is initialized by {@link setConfig()}
  12632. * @var PEAR_Registry
  12633. * @access private
  12634. */
  12635. var $_registry;
  12636. /**
  12637. * Filename of the dependency DB (usually .depdb)
  12638. * @var string
  12639. * @access private
  12640. */
  12641. var $_depdb = false;
  12642. /**
  12643. * File name of the lockfile (usually .depdblock)
  12644. * @var string
  12645. * @access private
  12646. */
  12647. var $_lockfile = false;
  12648. /**
  12649. * Open file resource for locking the lockfile
  12650. * @var resource|false
  12651. * @access private
  12652. */
  12653. var $_lockFp = false;
  12654. /**
  12655. * API version of this class, used to validate a file on-disk
  12656. * @var string
  12657. * @access private
  12658. */
  12659. var $_version = '1.0';
  12660. /**
  12661. * Cached dependency database file
  12662. * @var array|null
  12663. * @access private
  12664. */
  12665. var $_cache;
  12666. // }}}
  12667. // {{{ & singleton()
  12668. /**
  12669. * Get a raw dependency database. Calls setConfig() and assertDepsDB()
  12670. * @param PEAR_Config
  12671. * @param string|false full path to the dependency database, or false to use default
  12672. * @return PEAR_DependencyDB|PEAR_Error
  12673. */
  12674. public static function &singleton(&$config, $depdb = false)
  12675. {
  12676. $phpdir = $config->get('php_dir', null, 'pear.php.net');
  12677. if (!isset($GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir])) {
  12678. $a = new PEAR_DependencyDB;
  12679. $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir] = &$a;
  12680. $a->setConfig($config, $depdb);
  12681. $e = $a->assertDepsDB();
  12682. if (PEAR::isError($e)) {
  12683. return $e;
  12684. }
  12685. }
  12686. return $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir];
  12687. }
  12688. /**
  12689. * Set up the registry/location of dependency DB
  12690. * @param PEAR_Config|false
  12691. * @param string|false full path to the dependency database, or false to use default
  12692. */
  12693. function setConfig(&$config, $depdb = false)
  12694. {
  12695. if (!$config) {
  12696. $this->_config = &PEAR_Config::singleton();
  12697. } else {
  12698. $this->_config = &$config;
  12699. }
  12700. $this->_registry = &$this->_config->getRegistry();
  12701. if (!$depdb) {
  12702. $dir = $this->_config->get('metadata_dir', null, 'pear.php.net');
  12703. if (!$dir) {
  12704. $dir = $this->_config->get('php_dir', null, 'pear.php.net');
  12705. }
  12706. $this->_depdb = $dir . DIRECTORY_SEPARATOR . '.depdb';
  12707. } else {
  12708. $this->_depdb = $depdb;
  12709. }
  12710. $this->_lockfile = dirname($this->_depdb) . DIRECTORY_SEPARATOR . '.depdblock';
  12711. }
  12712. // }}}
  12713. function hasWriteAccess()
  12714. {
  12715. if (!file_exists($this->_depdb)) {
  12716. $dir = $this->_depdb;
  12717. while ($dir && $dir != '.') {
  12718. $dir = dirname($dir); // cd ..
  12719. if ($dir != '.' && file_exists($dir)) {
  12720. if (is_writeable($dir)) {
  12721. return true;
  12722. }
  12723. return false;
  12724. }
  12725. }
  12726. return false;
  12727. }
  12728. return is_writeable($this->_depdb);
  12729. }
  12730. // {{{ assertDepsDB()
  12731. /**
  12732. * Create the dependency database, if it doesn't exist. Error if the database is
  12733. * newer than the code reading it.
  12734. * @return void|PEAR_Error
  12735. */
  12736. function assertDepsDB()
  12737. {
  12738. if (!is_file($this->_depdb)) {
  12739. $this->rebuildDB();
  12740. return;
  12741. }
  12742. $depdb = $this->_getDepDB();
  12743. // Datatype format has been changed, rebuild the Deps DB
  12744. if ($depdb['_version'] < $this->_version) {
  12745. $this->rebuildDB();
  12746. }
  12747. if ($depdb['_version'][0] > $this->_version[0]) {
  12748. return PEAR::raiseError('Dependency database is version ' .
  12749. $depdb['_version'] . ', and we are version ' .
  12750. $this->_version . ', cannot continue');
  12751. }
  12752. }
  12753. /**
  12754. * Get a list of installed packages that depend on this package
  12755. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
  12756. * @return array|false
  12757. */
  12758. function getDependentPackages(&$pkg)
  12759. {
  12760. $data = $this->_getDepDB();
  12761. if (is_object($pkg)) {
  12762. $channel = strtolower($pkg->getChannel());
  12763. $package = strtolower($pkg->getPackage());
  12764. } else {
  12765. $channel = strtolower($pkg['channel']);
  12766. $package = strtolower($pkg['package']);
  12767. }
  12768. if (isset($data['packages'][$channel][$package])) {
  12769. return $data['packages'][$channel][$package];
  12770. }
  12771. return false;
  12772. }
  12773. /**
  12774. * Get a list of the actual dependencies of installed packages that depend on
  12775. * a package.
  12776. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
  12777. * @return array|false
  12778. */
  12779. function getDependentPackageDependencies(&$pkg)
  12780. {
  12781. $data = $this->_getDepDB();
  12782. if (is_object($pkg)) {
  12783. $channel = strtolower($pkg->getChannel());
  12784. $package = strtolower($pkg->getPackage());
  12785. } else if (is_array($pkg)) {
  12786. $channel = strtolower($pkg['channel']);
  12787. $package = strtolower($pkg['package']);
  12788. } else {
  12789. return false;
  12790. }
  12791. $depend = $this->getDependentPackages($pkg);
  12792. if (!$depend) {
  12793. return false;
  12794. }
  12795. $dependencies = array();
  12796. foreach ($depend as $info) {
  12797. $temp = $this->getDependencies($info);
  12798. foreach ($temp as $dep) {
  12799. if (
  12800. isset($dep['dep'], $dep['dep']['channel'], $dep['dep']['name']) &&
  12801. strtolower($dep['dep']['channel']) == $channel &&
  12802. strtolower($dep['dep']['name']) == $package
  12803. ) {
  12804. if (!isset($dependencies[$info['channel']])) {
  12805. $dependencies[$info['channel']] = array();
  12806. }
  12807. if (!isset($dependencies[$info['channel']][$info['package']])) {
  12808. $dependencies[$info['channel']][$info['package']] = array();
  12809. }
  12810. $dependencies[$info['channel']][$info['package']][] = $dep;
  12811. }
  12812. }
  12813. }
  12814. return $dependencies;
  12815. }
  12816. /**
  12817. * Get a list of dependencies of this installed package
  12818. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
  12819. * @return array|false
  12820. */
  12821. function getDependencies(&$pkg)
  12822. {
  12823. if (is_object($pkg)) {
  12824. $channel = strtolower($pkg->getChannel());
  12825. $package = strtolower($pkg->getPackage());
  12826. } else {
  12827. $channel = strtolower($pkg['channel']);
  12828. $package = strtolower($pkg['package']);
  12829. }
  12830. $data = $this->_getDepDB();
  12831. if (isset($data['dependencies'][$channel][$package])) {
  12832. return $data['dependencies'][$channel][$package];
  12833. }
  12834. return false;
  12835. }
  12836. /**
  12837. * Determine whether $parent depends on $child, near or deep
  12838. * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
  12839. * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
  12840. */
  12841. function dependsOn($parent, $child)
  12842. {
  12843. $c = array();
  12844. $this->_getDepDB();
  12845. return $this->_dependsOn($parent, $child, $c);
  12846. }
  12847. function _dependsOn($parent, $child, &$checked)
  12848. {
  12849. if (is_object($parent)) {
  12850. $channel = strtolower($parent->getChannel());
  12851. $package = strtolower($parent->getPackage());
  12852. } else {
  12853. $channel = strtolower($parent['channel']);
  12854. $package = strtolower($parent['package']);
  12855. }
  12856. if (is_object($child)) {
  12857. $depchannel = strtolower($child->getChannel());
  12858. $deppackage = strtolower($child->getPackage());
  12859. } else {
  12860. $depchannel = strtolower($child['channel']);
  12861. $deppackage = strtolower($child['package']);
  12862. }
  12863. if (isset($checked[$channel][$package][$depchannel][$deppackage])) {
  12864. return false; // avoid endless recursion
  12865. }
  12866. $checked[$channel][$package][$depchannel][$deppackage] = true;
  12867. if (!isset($this->_cache['dependencies'][$channel][$package])) {
  12868. return false;
  12869. }
  12870. foreach ($this->_cache['dependencies'][$channel][$package] as $info) {
  12871. if (isset($info['dep']['uri'])) {
  12872. if (is_object($child)) {
  12873. if ($info['dep']['uri'] == $child->getURI()) {
  12874. return true;
  12875. }
  12876. } elseif (isset($child['uri'])) {
  12877. if ($info['dep']['uri'] == $child['uri']) {
  12878. return true;
  12879. }
  12880. }
  12881. return false;
  12882. }
  12883. if (strtolower($info['dep']['channel']) == $depchannel &&
  12884. strtolower($info['dep']['name']) == $deppackage) {
  12885. return true;
  12886. }
  12887. }
  12888. foreach ($this->_cache['dependencies'][$channel][$package] as $info) {
  12889. if (isset($info['dep']['uri'])) {
  12890. if ($this->_dependsOn(array(
  12891. 'uri' => $info['dep']['uri'],
  12892. 'package' => $info['dep']['name']), $child, $checked)) {
  12893. return true;
  12894. }
  12895. } else {
  12896. if ($this->_dependsOn(array(
  12897. 'channel' => $info['dep']['channel'],
  12898. 'package' => $info['dep']['name']), $child, $checked)) {
  12899. return true;
  12900. }
  12901. }
  12902. }
  12903. return false;
  12904. }
  12905. /**
  12906. * Register dependencies of a package that is being installed or upgraded
  12907. * @param PEAR_PackageFile_v2|PEAR_PackageFile_v2
  12908. */
  12909. function installPackage(&$package)
  12910. {
  12911. $data = $this->_getDepDB();
  12912. unset($this->_cache);
  12913. $this->_setPackageDeps($data, $package);
  12914. $this->_writeDepDB($data);
  12915. }
  12916. /**
  12917. * Remove dependencies of a package that is being uninstalled, or upgraded.
  12918. *
  12919. * Upgraded packages first uninstall, then install
  12920. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array If an array, then it must have
  12921. * indices 'channel' and 'package'
  12922. */
  12923. function uninstallPackage(&$pkg)
  12924. {
  12925. $data = $this->_getDepDB();
  12926. unset($this->_cache);
  12927. if (is_object($pkg)) {
  12928. $channel = strtolower($pkg->getChannel());
  12929. $package = strtolower($pkg->getPackage());
  12930. } else {
  12931. $channel = strtolower($pkg['channel']);
  12932. $package = strtolower($pkg['package']);
  12933. }
  12934. if (!isset($data['dependencies'][$channel][$package])) {
  12935. return true;
  12936. }
  12937. foreach ($data['dependencies'][$channel][$package] as $dep) {
  12938. $found = false;
  12939. $depchannel = isset($dep['dep']['uri']) ? '__uri' : strtolower($dep['dep']['channel']);
  12940. $depname = strtolower($dep['dep']['name']);
  12941. if (isset($data['packages'][$depchannel][$depname])) {
  12942. foreach ($data['packages'][$depchannel][$depname] as $i => $info) {
  12943. if ($info['channel'] == $channel && $info['package'] == $package) {
  12944. $found = true;
  12945. break;
  12946. }
  12947. }
  12948. }
  12949. if ($found) {
  12950. unset($data['packages'][$depchannel][$depname][$i]);
  12951. if (!count($data['packages'][$depchannel][$depname])) {
  12952. unset($data['packages'][$depchannel][$depname]);
  12953. if (!count($data['packages'][$depchannel])) {
  12954. unset($data['packages'][$depchannel]);
  12955. }
  12956. } else {
  12957. $data['packages'][$depchannel][$depname] =
  12958. array_values($data['packages'][$depchannel][$depname]);
  12959. }
  12960. }
  12961. }
  12962. unset($data['dependencies'][$channel][$package]);
  12963. if (!count($data['dependencies'][$channel])) {
  12964. unset($data['dependencies'][$channel]);
  12965. }
  12966. if (!count($data['dependencies'])) {
  12967. unset($data['dependencies']);
  12968. }
  12969. if (!count($data['packages'])) {
  12970. unset($data['packages']);
  12971. }
  12972. $this->_writeDepDB($data);
  12973. }
  12974. /**
  12975. * Rebuild the dependency DB by reading registry entries.
  12976. * @return true|PEAR_Error
  12977. */
  12978. function rebuildDB()
  12979. {
  12980. $depdb = array('_version' => $this->_version);
  12981. if (!$this->hasWriteAccess()) {
  12982. // allow startup for read-only with older Registry
  12983. return $depdb;
  12984. }
  12985. $packages = $this->_registry->listAllPackages();
  12986. if (PEAR::isError($packages)) {
  12987. return $packages;
  12988. }
  12989. foreach ($packages as $channel => $ps) {
  12990. foreach ($ps as $package) {
  12991. $package = $this->_registry->getPackage($package, $channel);
  12992. if (PEAR::isError($package)) {
  12993. return $package;
  12994. }
  12995. $this->_setPackageDeps($depdb, $package);
  12996. }
  12997. }
  12998. $error = $this->_writeDepDB($depdb);
  12999. if (PEAR::isError($error)) {
  13000. return $error;
  13001. }
  13002. $this->_cache = $depdb;
  13003. return true;
  13004. }
  13005. /**
  13006. * Register usage of the dependency DB to prevent race conditions
  13007. * @param int one of the LOCK_* constants
  13008. * @return true|PEAR_Error
  13009. * @access private
  13010. */
  13011. function _lock($mode = LOCK_EX)
  13012. {
  13013. if (stristr(php_uname(), 'Windows 9')) {
  13014. return true;
  13015. }
  13016. if ($mode != LOCK_UN && is_resource($this->_lockFp)) {
  13017. // XXX does not check type of lock (LOCK_SH/LOCK_EX)
  13018. return true;
  13019. }
  13020. $open_mode = 'w';
  13021. // XXX People reported problems with LOCK_SH and 'w'
  13022. if ($mode === LOCK_SH) {
  13023. if (!file_exists($this->_lockfile)) {
  13024. touch($this->_lockfile);
  13025. } elseif (!is_file($this->_lockfile)) {
  13026. return PEAR::raiseError('could not create Dependency lock file, ' .
  13027. 'it exists and is not a regular file');
  13028. }
  13029. $open_mode = 'r';
  13030. }
  13031. if (!is_resource($this->_lockFp)) {
  13032. $this->_lockFp = @fopen($this->_lockfile, $open_mode);
  13033. }
  13034. if (!is_resource($this->_lockFp)) {
  13035. $last_errormsg = '';
  13036. $last_error = error_get_last();
  13037. if (!empty($last_error['message'])) {
  13038. $last_errormsg = $last_error['message'];
  13039. }
  13040. return PEAR::raiseError("could not create Dependency lock file" .
  13041. (isset($last_errormsg) ? ": " . $last_errormsg : ""));
  13042. }
  13043. if (!(int)flock($this->_lockFp, $mode)) {
  13044. switch ($mode) {
  13045. case LOCK_SH: $str = 'shared'; break;
  13046. case LOCK_EX: $str = 'exclusive'; break;
  13047. case LOCK_UN: $str = 'unlock'; break;
  13048. default: $str = 'unknown'; break;
  13049. }
  13050. return PEAR::raiseError("could not acquire $str lock ($this->_lockfile)");
  13051. }
  13052. return true;
  13053. }
  13054. /**
  13055. * Release usage of dependency DB
  13056. * @return true|PEAR_Error
  13057. * @access private
  13058. */
  13059. function _unlock()
  13060. {
  13061. $ret = $this->_lock(LOCK_UN);
  13062. if (is_resource($this->_lockFp)) {
  13063. fclose($this->_lockFp);
  13064. }
  13065. $this->_lockFp = null;
  13066. return $ret;
  13067. }
  13068. /**
  13069. * Load the dependency database from disk, or return the cache
  13070. * @return array|PEAR_Error
  13071. */
  13072. function _getDepDB()
  13073. {
  13074. if (!$this->hasWriteAccess()) {
  13075. return array('_version' => $this->_version);
  13076. }
  13077. if (isset($this->_cache)) {
  13078. return $this->_cache;
  13079. }
  13080. if (!$fp = fopen($this->_depdb, 'r')) {
  13081. $err = PEAR::raiseError("Could not open dependencies file `".$this->_depdb."'");
  13082. return $err;
  13083. }
  13084. clearstatcache();
  13085. fclose($fp);
  13086. $data = @unserialize(file_get_contents($this->_depdb));
  13087. $this->_cache = $data;
  13088. return $data;
  13089. }
  13090. /**
  13091. * Write out the dependency database to disk
  13092. * @param array the database
  13093. * @return true|PEAR_Error
  13094. * @access private
  13095. */
  13096. function _writeDepDB(&$deps)
  13097. {
  13098. if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  13099. return $e;
  13100. }
  13101. if (!$fp = fopen($this->_depdb, 'wb')) {
  13102. $this->_unlock();
  13103. return PEAR::raiseError("Could not open dependencies file `".$this->_depdb."' for writing");
  13104. }
  13105. fwrite($fp, serialize($deps));
  13106. fclose($fp);
  13107. $this->_unlock();
  13108. $this->_cache = $deps;
  13109. return true;
  13110. }
  13111. /**
  13112. * Register all dependencies from a package in the dependencies database, in essence
  13113. * "installing" the package's dependency information
  13114. * @param array the database
  13115. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  13116. * @access private
  13117. */
  13118. function _setPackageDeps(&$data, &$pkg)
  13119. {
  13120. $pkg->setConfig($this->_config);
  13121. if ($pkg->getPackagexmlVersion() == '1.0') {
  13122. $gen = &$pkg->getDefaultGenerator();
  13123. $deps = $gen->dependenciesToV2();
  13124. } else {
  13125. $deps = $pkg->getDeps(true);
  13126. }
  13127. if (!$deps) {
  13128. return;
  13129. }
  13130. if (!is_array($data)) {
  13131. $data = array();
  13132. }
  13133. if (!isset($data['dependencies'])) {
  13134. $data['dependencies'] = array();
  13135. }
  13136. $channel = strtolower($pkg->getChannel());
  13137. $package = strtolower($pkg->getPackage());
  13138. if (!isset($data['dependencies'][$channel])) {
  13139. $data['dependencies'][$channel] = array();
  13140. }
  13141. $data['dependencies'][$channel][$package] = array();
  13142. if (isset($deps['required']['package'])) {
  13143. if (!isset($deps['required']['package'][0])) {
  13144. $deps['required']['package'] = array($deps['required']['package']);
  13145. }
  13146. foreach ($deps['required']['package'] as $dep) {
  13147. $this->_registerDep($data, $pkg, $dep, 'required');
  13148. }
  13149. }
  13150. if (isset($deps['optional']['package'])) {
  13151. if (!isset($deps['optional']['package'][0])) {
  13152. $deps['optional']['package'] = array($deps['optional']['package']);
  13153. }
  13154. foreach ($deps['optional']['package'] as $dep) {
  13155. $this->_registerDep($data, $pkg, $dep, 'optional');
  13156. }
  13157. }
  13158. if (isset($deps['required']['subpackage'])) {
  13159. if (!isset($deps['required']['subpackage'][0])) {
  13160. $deps['required']['subpackage'] = array($deps['required']['subpackage']);
  13161. }
  13162. foreach ($deps['required']['subpackage'] as $dep) {
  13163. $this->_registerDep($data, $pkg, $dep, 'required');
  13164. }
  13165. }
  13166. if (isset($deps['optional']['subpackage'])) {
  13167. if (!isset($deps['optional']['subpackage'][0])) {
  13168. $deps['optional']['subpackage'] = array($deps['optional']['subpackage']);
  13169. }
  13170. foreach ($deps['optional']['subpackage'] as $dep) {
  13171. $this->_registerDep($data, $pkg, $dep, 'optional');
  13172. }
  13173. }
  13174. if (isset($deps['group'])) {
  13175. if (!isset($deps['group'][0])) {
  13176. $deps['group'] = array($deps['group']);
  13177. }
  13178. foreach ($deps['group'] as $group) {
  13179. if (isset($group['package'])) {
  13180. if (!isset($group['package'][0])) {
  13181. $group['package'] = array($group['package']);
  13182. }
  13183. foreach ($group['package'] as $dep) {
  13184. $this->_registerDep($data, $pkg, $dep, 'optional',
  13185. $group['attribs']['name']);
  13186. }
  13187. }
  13188. if (isset($group['subpackage'])) {
  13189. if (!isset($group['subpackage'][0])) {
  13190. $group['subpackage'] = array($group['subpackage']);
  13191. }
  13192. foreach ($group['subpackage'] as $dep) {
  13193. $this->_registerDep($data, $pkg, $dep, 'optional',
  13194. $group['attribs']['name']);
  13195. }
  13196. }
  13197. }
  13198. }
  13199. if ($data['dependencies'][$channel][$package] == array()) {
  13200. unset($data['dependencies'][$channel][$package]);
  13201. if (!count($data['dependencies'][$channel])) {
  13202. unset($data['dependencies'][$channel]);
  13203. }
  13204. }
  13205. }
  13206. /**
  13207. * @param array the database
  13208. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  13209. * @param array the specific dependency
  13210. * @param required|optional whether this is a required or an optional dep
  13211. * @param string|false dependency group this dependency is from, or false for ordinary dep
  13212. */
  13213. function _registerDep(&$data, &$pkg, $dep, $type, $group = false)
  13214. {
  13215. $info = array(
  13216. 'dep' => $dep,
  13217. 'type' => $type,
  13218. 'group' => $group
  13219. );
  13220. $dep = array_map('strtolower', $dep);
  13221. $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
  13222. if (!isset($data['dependencies'])) {
  13223. $data['dependencies'] = array();
  13224. }
  13225. $channel = strtolower($pkg->getChannel());
  13226. $package = strtolower($pkg->getPackage());
  13227. if (!isset($data['dependencies'][$channel])) {
  13228. $data['dependencies'][$channel] = array();
  13229. }
  13230. if (!isset($data['dependencies'][$channel][$package])) {
  13231. $data['dependencies'][$channel][$package] = array();
  13232. }
  13233. $data['dependencies'][$channel][$package][] = $info;
  13234. if (isset($data['packages'][$depchannel][$dep['name']])) {
  13235. $found = false;
  13236. foreach ($data['packages'][$depchannel][$dep['name']] as $i => $p) {
  13237. if ($p['channel'] == $channel && $p['package'] == $package) {
  13238. $found = true;
  13239. break;
  13240. }
  13241. }
  13242. } else {
  13243. if (!isset($data['packages'])) {
  13244. $data['packages'] = array();
  13245. }
  13246. if (!isset($data['packages'][$depchannel])) {
  13247. $data['packages'][$depchannel] = array();
  13248. }
  13249. if (!isset($data['packages'][$depchannel][$dep['name']])) {
  13250. $data['packages'][$depchannel][$dep['name']] = array();
  13251. }
  13252. $found = false;
  13253. }
  13254. if (!$found) {
  13255. $data['packages'][$depchannel][$dep['name']][] = array(
  13256. 'channel' => $channel,
  13257. 'package' => $package
  13258. );
  13259. }
  13260. }
  13261. }
  13262. <?php
  13263. /**
  13264. * PEAR_Downloader, the PEAR Installer's download utility class
  13265. *
  13266. * PHP versions 4 and 5
  13267. *
  13268. * @category pear
  13269. * @package PEAR
  13270. * @author Greg Beaver <cellog@php.net>
  13271. * @author Stig Bakken <ssb@php.net>
  13272. * @author Tomas V. V. Cox <cox@idecnet.com>
  13273. * @author Martin Jansen <mj@php.net>
  13274. * @copyright 1997-2009 The Authors
  13275. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  13276. * @link http://pear.php.net/package/PEAR
  13277. * @since File available since Release 1.3.0
  13278. */
  13279. /**
  13280. * Needed for constants, extending
  13281. */
  13282. require_once 'phar://go-pear.phar/' . 'PEAR/Common.php';
  13283. require_once 'phar://go-pear.phar/' . 'PEAR/Proxy.php';
  13284. define('PEAR_INSTALLER_OK', 1);
  13285. define('PEAR_INSTALLER_FAILED', 0);
  13286. define('PEAR_INSTALLER_SKIPPED', -1);
  13287. define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2);
  13288. /**
  13289. * Administration class used to download anything from the internet (PEAR Packages,
  13290. * static URLs, xml files)
  13291. *
  13292. * @category pear
  13293. * @package PEAR
  13294. * @author Greg Beaver <cellog@php.net>
  13295. * @author Stig Bakken <ssb@php.net>
  13296. * @author Tomas V. V. Cox <cox@idecnet.com>
  13297. * @author Martin Jansen <mj@php.net>
  13298. * @copyright 1997-2009 The Authors
  13299. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  13300. * @version Release: 1.10.16
  13301. * @link http://pear.php.net/package/PEAR
  13302. * @since Class available since Release 1.3.0
  13303. */
  13304. class PEAR_Downloader extends PEAR_Common
  13305. {
  13306. /**
  13307. * @var PEAR_Registry
  13308. * @access private
  13309. */
  13310. var $_registry;
  13311. /**
  13312. * Preferred Installation State (snapshot, devel, alpha, beta, stable)
  13313. * @var string|null
  13314. * @access private
  13315. */
  13316. var $_preferredState;
  13317. /**
  13318. * Options from command-line passed to Install.
  13319. *
  13320. * Recognized options:<br />
  13321. * - onlyreqdeps : install all required dependencies as well
  13322. * - alldeps : install all dependencies, including optional
  13323. * - installroot : base relative path to install files in
  13324. * - force : force a download even if warnings would prevent it
  13325. * - nocompress : download uncompressed tarballs
  13326. * - configureoptions : additional configure options
  13327. * @see PEAR_Command_Install
  13328. * @access private
  13329. * @var array
  13330. */
  13331. var $_options;
  13332. /**
  13333. * Downloaded Packages after a call to download().
  13334. *
  13335. * Format of each entry:
  13336. *
  13337. * <code>
  13338. * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
  13339. * 'info' => array() // parsed package.xml
  13340. * );
  13341. * </code>
  13342. * @access private
  13343. * @var array
  13344. */
  13345. var $_downloadedPackages = array();
  13346. /**
  13347. * Packages slated for download.
  13348. *
  13349. * This is used to prevent downloading a package more than once should it be a dependency
  13350. * for two packages to be installed.
  13351. * Format of each entry:
  13352. *
  13353. * <pre>
  13354. * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
  13355. * );
  13356. * </pre>
  13357. * @access private
  13358. * @var array
  13359. */
  13360. var $_toDownload = array();
  13361. /**
  13362. * Array of every package installed, with names lower-cased.
  13363. *
  13364. * Format:
  13365. * <code>
  13366. * array('package1' => 0, 'package2' => 1, );
  13367. * </code>
  13368. * @var array
  13369. */
  13370. var $_installed = array();
  13371. /**
  13372. * @var array
  13373. * @access private
  13374. */
  13375. var $_errorStack = array();
  13376. /**
  13377. * @var boolean
  13378. * @access private
  13379. */
  13380. var $_internalDownload = false;
  13381. /**
  13382. * Temporary variable used in sorting packages by dependency in {@link sortPkgDeps()}
  13383. * @var array
  13384. * @access private
  13385. */
  13386. var $_packageSortTree;
  13387. /**
  13388. * Temporary directory, or configuration value where downloads will occur
  13389. * @var string
  13390. */
  13391. var $_downloadDir;
  13392. /**
  13393. * List of methods that can be called both statically and non-statically.
  13394. * @var array
  13395. */
  13396. protected static $bivalentMethods = array(
  13397. 'setErrorHandling' => true,
  13398. 'raiseError' => true,
  13399. 'throwError' => true,
  13400. 'pushErrorHandling' => true,
  13401. 'popErrorHandling' => true,
  13402. 'downloadHttp' => true,
  13403. );
  13404. /**
  13405. * @param PEAR_Frontend_*
  13406. * @param array
  13407. * @param PEAR_Config
  13408. */
  13409. function __construct($ui = null, $options = array(), $config = null)
  13410. {
  13411. parent::__construct();
  13412. $this->_options = $options;
  13413. if ($config !== null) {
  13414. $this->config = &$config;
  13415. $this->_preferredState = $this->config->get('preferred_state');
  13416. }
  13417. $this->ui = &$ui;
  13418. if (!$this->_preferredState) {
  13419. // don't inadvertently use a non-set preferred_state
  13420. $this->_preferredState = null;
  13421. }
  13422. if ($config !== null) {
  13423. if (isset($this->_options['installroot'])) {
  13424. $this->config->setInstallRoot($this->_options['installroot']);
  13425. }
  13426. $this->_registry = &$config->getRegistry();
  13427. }
  13428. if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
  13429. $this->_installed = $this->_registry->listAllPackages();
  13430. foreach ($this->_installed as $key => $unused) {
  13431. if (!count($unused)) {
  13432. continue;
  13433. }
  13434. $strtolower = function($a) { return strtolower($a); };
  13435. array_walk($this->_installed[$key], $strtolower);
  13436. }
  13437. }
  13438. }
  13439. /**
  13440. * Attempt to discover a channel's remote capabilities from
  13441. * its server name
  13442. * @param string
  13443. * @return boolean
  13444. */
  13445. function discover($channel)
  13446. {
  13447. $this->log(1, 'Attempting to discover channel "' . $channel . '"...');
  13448. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  13449. $callback = $this->ui ? array(&$this, '_downloadCallback') : null;
  13450. if (!class_exists('System')) {
  13451. require_once 'phar://go-pear.phar/' . 'System.php';
  13452. }
  13453. $tmpdir = $this->config->get('temp_dir');
  13454. $tmp = System::mktemp('-d -t "' . $tmpdir . '"');
  13455. $a = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false);
  13456. PEAR::popErrorHandling();
  13457. if (PEAR::isError($a)) {
  13458. // Attempt to fallback to https automatically.
  13459. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  13460. $this->log(1, 'Attempting fallback to https instead of http on channel "' . $channel . '"...');
  13461. $a = $this->downloadHttp('https://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false);
  13462. PEAR::popErrorHandling();
  13463. if (PEAR::isError($a)) {
  13464. return false;
  13465. }
  13466. }
  13467. list($a, $lastmodified) = $a;
  13468. if (!class_exists('PEAR_ChannelFile')) {
  13469. require_once 'phar://go-pear.phar/' . 'PEAR/ChannelFile.php';
  13470. }
  13471. $b = new PEAR_ChannelFile;
  13472. if ($b->fromXmlFile($a)) {
  13473. unlink($a);
  13474. if ($this->config->get('auto_discover')) {
  13475. $this->_registry->addChannel($b, $lastmodified);
  13476. $alias = $b->getName();
  13477. if ($b->getName() == $this->_registry->channelName($b->getAlias())) {
  13478. $alias = $b->getAlias();
  13479. }
  13480. $this->log(1, 'Auto-discovered channel "' . $channel .
  13481. '", alias "' . $alias . '", adding to registry');
  13482. }
  13483. return true;
  13484. }
  13485. unlink($a);
  13486. return false;
  13487. }
  13488. /**
  13489. * For simpler unit-testing
  13490. * @param PEAR_Downloader
  13491. * @return PEAR_Downloader_Package
  13492. */
  13493. function newDownloaderPackage(&$t)
  13494. {
  13495. if (!class_exists('PEAR_Downloader_Package')) {
  13496. require_once 'phar://go-pear.phar/' . 'PEAR/Downloader/Package.php';
  13497. }
  13498. $a = new PEAR_Downloader_Package($t);
  13499. return $a;
  13500. }
  13501. /**
  13502. * For simpler unit-testing
  13503. * @param PEAR_Config
  13504. * @param array
  13505. * @param array
  13506. * @param int
  13507. */
  13508. function &getDependency2Object(&$c, $i, $p, $s)
  13509. {
  13510. if (!class_exists('PEAR_Dependency2')) {
  13511. require_once 'phar://go-pear.phar/' . 'PEAR/Dependency2.php';
  13512. }
  13513. $z = new PEAR_Dependency2($c, $i, $p, $s);
  13514. return $z;
  13515. }
  13516. function &download($params)
  13517. {
  13518. if (!count($params)) {
  13519. $a = array();
  13520. return $a;
  13521. }
  13522. if (!isset($this->_registry)) {
  13523. $this->_registry = &$this->config->getRegistry();
  13524. }
  13525. $channelschecked = array();
  13526. // convert all parameters into PEAR_Downloader_Package objects
  13527. foreach ($params as $i => $param) {
  13528. $params[$i] = $this->newDownloaderPackage($this);
  13529. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  13530. $err = $params[$i]->initialize($param);
  13531. PEAR::staticPopErrorHandling();
  13532. if (!$err) {
  13533. // skip parameters that were missed by preferred_state
  13534. continue;
  13535. }
  13536. if (PEAR::isError($err)) {
  13537. if (!isset($this->_options['soft']) && $err->getMessage() !== '') {
  13538. $this->log(0, $err->getMessage());
  13539. }
  13540. $params[$i] = false;
  13541. if (is_object($param)) {
  13542. $param = $param->getChannel() . '/' . $param->getPackage();
  13543. }
  13544. if (!isset($this->_options['soft'])) {
  13545. $this->log(2, 'Package "' . $param . '" is not valid');
  13546. }
  13547. // Message logged above in a specific verbose mode, passing null to not show up on CLI
  13548. $this->pushError(null, PEAR_INSTALLER_SKIPPED);
  13549. } else {
  13550. do {
  13551. if ($params[$i] && $params[$i]->getType() == 'local') {
  13552. // bug #7090 skip channel.xml check for local packages
  13553. break;
  13554. }
  13555. if ($params[$i] && !isset($channelschecked[$params[$i]->getChannel()]) &&
  13556. !isset($this->_options['offline'])
  13557. ) {
  13558. $channelschecked[$params[$i]->getChannel()] = true;
  13559. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  13560. if (!class_exists('System')) {
  13561. require_once 'phar://go-pear.phar/' . 'System.php';
  13562. }
  13563. $curchannel = $this->_registry->getChannel($params[$i]->getChannel());
  13564. if (PEAR::isError($curchannel)) {
  13565. PEAR::staticPopErrorHandling();
  13566. return $this->raiseError($curchannel);
  13567. }
  13568. if (PEAR::isError($dir = $this->getDownloadDir())) {
  13569. PEAR::staticPopErrorHandling();
  13570. break;
  13571. }
  13572. $mirror = $this->config->get('preferred_mirror', null, $params[$i]->getChannel());
  13573. $url = 'http://' . $mirror . '/channel.xml';
  13574. $a = $this->downloadHttp($url, $this->ui, $dir, null, $curchannel->lastModified());
  13575. PEAR::staticPopErrorHandling();
  13576. if ($a === false) {
  13577. //channel.xml not modified
  13578. break;
  13579. } else if (PEAR::isError($a)) {
  13580. // Attempt fallback to https automatically
  13581. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  13582. $a = $this->downloadHttp('https://' . $mirror .
  13583. '/channel.xml', $this->ui, $dir, null, $curchannel->lastModified());
  13584. PEAR::staticPopErrorHandling();
  13585. if (PEAR::isError($a) || !$a) {
  13586. break;
  13587. }
  13588. }
  13589. $this->log(0, 'WARNING: channel "' . $params[$i]->getChannel() . '" has ' .
  13590. 'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $params[$i]->getChannel() .
  13591. '" to update');
  13592. }
  13593. } while (false);
  13594. if ($params[$i] && !isset($this->_options['downloadonly'])) {
  13595. if (isset($this->_options['packagingroot'])) {
  13596. $checkdir = $this->_prependPath(
  13597. $this->config->get('php_dir', null, $params[$i]->getChannel()),
  13598. $this->_options['packagingroot']);
  13599. } else {
  13600. $checkdir = $this->config->get('php_dir',
  13601. null, $params[$i]->getChannel());
  13602. }
  13603. while ($checkdir && $checkdir != '/' && !file_exists($checkdir)) {
  13604. $checkdir = dirname($checkdir);
  13605. }
  13606. if ($checkdir == '.') {
  13607. $checkdir = '/';
  13608. }
  13609. if (!is_writeable($checkdir)) {
  13610. return PEAR::raiseError('Cannot install, php_dir for channel "' .
  13611. $params[$i]->getChannel() . '" is not writeable by the current user');
  13612. }
  13613. }
  13614. }
  13615. }
  13616. unset($channelschecked);
  13617. PEAR_Downloader_Package::removeDuplicates($params);
  13618. if (!count($params)) {
  13619. $a = array();
  13620. return $a;
  13621. }
  13622. if (!isset($this->_options['nodeps']) && !isset($this->_options['offline'])) {
  13623. $reverify = true;
  13624. while ($reverify) {
  13625. $reverify = false;
  13626. foreach ($params as $i => $param) {
  13627. //PHP Bug 40768 / PEAR Bug #10944
  13628. //Nested foreaches fail in PHP 5.2.1
  13629. key($params);
  13630. $ret = $params[$i]->detectDependencies($params);
  13631. if (PEAR::isError($ret)) {
  13632. $reverify = true;
  13633. $params[$i] = false;
  13634. PEAR_Downloader_Package::removeDuplicates($params);
  13635. if (!isset($this->_options['soft'])) {
  13636. $this->log(0, $ret->getMessage());
  13637. }
  13638. continue 2;
  13639. }
  13640. }
  13641. }
  13642. }
  13643. if (isset($this->_options['offline'])) {
  13644. $this->log(3, 'Skipping dependency download check, --offline specified');
  13645. }
  13646. if (!count($params)) {
  13647. $a = array();
  13648. return $a;
  13649. }
  13650. while (PEAR_Downloader_Package::mergeDependencies($params));
  13651. PEAR_Downloader_Package::removeDuplicates($params, true);
  13652. $errorparams = array();
  13653. if (PEAR_Downloader_Package::detectStupidDuplicates($params, $errorparams)) {
  13654. if (count($errorparams)) {
  13655. foreach ($errorparams as $param) {
  13656. $name = $this->_registry->parsedPackageNameToString($param->getParsedPackage());
  13657. $this->pushError('Duplicate package ' . $name . ' found', PEAR_INSTALLER_FAILED);
  13658. }
  13659. $a = array();
  13660. return $a;
  13661. }
  13662. }
  13663. PEAR_Downloader_Package::removeInstalled($params);
  13664. if (!count($params)) {
  13665. $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
  13666. $a = array();
  13667. return $a;
  13668. }
  13669. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  13670. $err = $this->analyzeDependencies($params);
  13671. PEAR::popErrorHandling();
  13672. if (!count($params)) {
  13673. $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
  13674. $a = array();
  13675. return $a;
  13676. }
  13677. $ret = array();
  13678. $newparams = array();
  13679. if (isset($this->_options['pretend'])) {
  13680. return $params;
  13681. }
  13682. $somefailed = false;
  13683. foreach ($params as $i => $package) {
  13684. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  13685. $pf = &$params[$i]->download();
  13686. PEAR::staticPopErrorHandling();
  13687. if (PEAR::isError($pf)) {
  13688. if (!isset($this->_options['soft'])) {
  13689. $this->log(1, $pf->getMessage());
  13690. $this->log(0, 'Error: cannot download "' .
  13691. $this->_registry->parsedPackageNameToString($package->getParsedPackage(),
  13692. true) .
  13693. '"');
  13694. }
  13695. $somefailed = true;
  13696. continue;
  13697. }
  13698. $newparams[] = &$params[$i];
  13699. $ret[] = array(
  13700. 'file' => $pf->getArchiveFile(),
  13701. 'info' => &$pf,
  13702. 'pkg' => $pf->getPackage()
  13703. );
  13704. }
  13705. if ($somefailed) {
  13706. // remove params that did not download successfully
  13707. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  13708. $err = $this->analyzeDependencies($newparams, true);
  13709. PEAR::popErrorHandling();
  13710. if (!count($newparams)) {
  13711. $this->pushError('Download failed', PEAR_INSTALLER_FAILED);
  13712. $a = array();
  13713. return $a;
  13714. }
  13715. }
  13716. $this->_downloadedPackages = $ret;
  13717. return $newparams;
  13718. }
  13719. /**
  13720. * @param array all packages to be installed
  13721. */
  13722. function analyzeDependencies(&$params, $force = false)
  13723. {
  13724. if (isset($this->_options['downloadonly'])) {
  13725. return;
  13726. }
  13727. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  13728. $redo = true;
  13729. $reset = $hasfailed = $failed = false;
  13730. while ($redo) {
  13731. $redo = false;
  13732. foreach ($params as $i => $param) {
  13733. $deps = $param->getDeps();
  13734. if (!$deps) {
  13735. $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(),
  13736. $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING);
  13737. $send = $param->getPackageFile();
  13738. $installcheck = $depchecker->validatePackage($send, $this, $params);
  13739. if (PEAR::isError($installcheck)) {
  13740. if (!isset($this->_options['soft'])) {
  13741. $this->log(0, $installcheck->getMessage());
  13742. }
  13743. $hasfailed = true;
  13744. $params[$i] = false;
  13745. $reset = true;
  13746. $redo = true;
  13747. $failed = false;
  13748. PEAR_Downloader_Package::removeDuplicates($params);
  13749. continue 2;
  13750. }
  13751. continue;
  13752. }
  13753. if (!$reset && $param->alreadyValidated() && !$force) {
  13754. continue;
  13755. }
  13756. if (count($deps)) {
  13757. $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(),
  13758. $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING);
  13759. $send = $param->getPackageFile();
  13760. if ($send === null) {
  13761. $send = $param->getDownloadURL();
  13762. }
  13763. $installcheck = $depchecker->validatePackage($send, $this, $params);
  13764. if (PEAR::isError($installcheck)) {
  13765. if (!isset($this->_options['soft'])) {
  13766. $this->log(0, $installcheck->getMessage());
  13767. }
  13768. $hasfailed = true;
  13769. $params[$i] = false;
  13770. $reset = true;
  13771. $redo = true;
  13772. $failed = false;
  13773. PEAR_Downloader_Package::removeDuplicates($params);
  13774. continue 2;
  13775. }
  13776. $failed = false;
  13777. if (isset($deps['required']) && is_array($deps['required'])) {
  13778. foreach ($deps['required'] as $type => $dep) {
  13779. // note: Dependency2 will never return a PEAR_Error if ignore-errors
  13780. // is specified, so soft is needed to turn off logging
  13781. if (!isset($dep[0])) {
  13782. if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($dep,
  13783. true, $params))) {
  13784. $failed = true;
  13785. if (!isset($this->_options['soft'])) {
  13786. $this->log(0, $e->getMessage());
  13787. }
  13788. } elseif (is_array($e) && !$param->alreadyValidated()) {
  13789. if (!isset($this->_options['soft'])) {
  13790. $this->log(0, $e[0]);
  13791. }
  13792. }
  13793. } else {
  13794. foreach ($dep as $d) {
  13795. if (PEAR::isError($e =
  13796. $depchecker->{"validate{$type}Dependency"}($d,
  13797. true, $params))) {
  13798. $failed = true;
  13799. if (!isset($this->_options['soft'])) {
  13800. $this->log(0, $e->getMessage());
  13801. }
  13802. } elseif (is_array($e) && !$param->alreadyValidated()) {
  13803. if (!isset($this->_options['soft'])) {
  13804. $this->log(0, $e[0]);
  13805. }
  13806. }
  13807. }
  13808. }
  13809. }
  13810. if (isset($deps['optional']) && is_array($deps['optional'])) {
  13811. foreach ($deps['optional'] as $type => $dep) {
  13812. if (!isset($dep[0])) {
  13813. if (PEAR::isError($e =
  13814. $depchecker->{"validate{$type}Dependency"}($dep,
  13815. false, $params))) {
  13816. $failed = true;
  13817. if (!isset($this->_options['soft'])) {
  13818. $this->log(0, $e->getMessage());
  13819. }
  13820. } elseif (is_array($e) && !$param->alreadyValidated()) {
  13821. if (!isset($this->_options['soft'])) {
  13822. $this->log(0, $e[0]);
  13823. }
  13824. }
  13825. } else {
  13826. foreach ($dep as $d) {
  13827. if (PEAR::isError($e =
  13828. $depchecker->{"validate{$type}Dependency"}($d,
  13829. false, $params))) {
  13830. $failed = true;
  13831. if (!isset($this->_options['soft'])) {
  13832. $this->log(0, $e->getMessage());
  13833. }
  13834. } elseif (is_array($e) && !$param->alreadyValidated()) {
  13835. if (!isset($this->_options['soft'])) {
  13836. $this->log(0, $e[0]);
  13837. }
  13838. }
  13839. }
  13840. }
  13841. }
  13842. }
  13843. $groupname = $param->getGroup();
  13844. if (isset($deps['group']) && $groupname) {
  13845. if (!isset($deps['group'][0])) {
  13846. $deps['group'] = array($deps['group']);
  13847. }
  13848. $found = false;
  13849. foreach ($deps['group'] as $group) {
  13850. if ($group['attribs']['name'] == $groupname) {
  13851. $found = true;
  13852. break;
  13853. }
  13854. }
  13855. if ($found) {
  13856. unset($group['attribs']);
  13857. foreach ($group as $type => $dep) {
  13858. if (!isset($dep[0])) {
  13859. if (PEAR::isError($e =
  13860. $depchecker->{"validate{$type}Dependency"}($dep,
  13861. false, $params))) {
  13862. $failed = true;
  13863. if (!isset($this->_options['soft'])) {
  13864. $this->log(0, $e->getMessage());
  13865. }
  13866. } elseif (is_array($e) && !$param->alreadyValidated()) {
  13867. if (!isset($this->_options['soft'])) {
  13868. $this->log(0, $e[0]);
  13869. }
  13870. }
  13871. } else {
  13872. foreach ($dep as $d) {
  13873. if (PEAR::isError($e =
  13874. $depchecker->{"validate{$type}Dependency"}($d,
  13875. false, $params))) {
  13876. $failed = true;
  13877. if (!isset($this->_options['soft'])) {
  13878. $this->log(0, $e->getMessage());
  13879. }
  13880. } elseif (is_array($e) && !$param->alreadyValidated()) {
  13881. if (!isset($this->_options['soft'])) {
  13882. $this->log(0, $e[0]);
  13883. }
  13884. }
  13885. }
  13886. }
  13887. }
  13888. }
  13889. }
  13890. } else {
  13891. foreach ($deps as $dep) {
  13892. if (PEAR::isError($e = $depchecker->validateDependency1($dep, $params))) {
  13893. $failed = true;
  13894. if (!isset($this->_options['soft'])) {
  13895. $this->log(0, $e->getMessage());
  13896. }
  13897. } elseif (is_array($e) && !$param->alreadyValidated()) {
  13898. if (!isset($this->_options['soft'])) {
  13899. $this->log(0, $e[0]);
  13900. }
  13901. }
  13902. }
  13903. }
  13904. $params[$i]->setValidated();
  13905. }
  13906. if ($failed) {
  13907. $hasfailed = true;
  13908. $params[$i] = false;
  13909. $reset = true;
  13910. $redo = true;
  13911. $failed = false;
  13912. PEAR_Downloader_Package::removeDuplicates($params);
  13913. continue 2;
  13914. }
  13915. }
  13916. }
  13917. PEAR::staticPopErrorHandling();
  13918. if ($hasfailed && (isset($this->_options['ignore-errors']) ||
  13919. isset($this->_options['nodeps']))) {
  13920. // this is probably not needed, but just in case
  13921. if (!isset($this->_options['soft'])) {
  13922. $this->log(0, 'WARNING: dependencies failed');
  13923. }
  13924. }
  13925. }
  13926. /**
  13927. * Retrieve the directory that downloads will happen in
  13928. * @access private
  13929. * @return string
  13930. */
  13931. function getDownloadDir()
  13932. {
  13933. if (isset($this->_downloadDir)) {
  13934. return $this->_downloadDir;
  13935. }
  13936. $downloaddir = $this->config->get('download_dir');
  13937. if (empty($downloaddir) || (is_dir($downloaddir) && !is_writable($downloaddir))) {
  13938. if (is_dir($downloaddir) && !is_writable($downloaddir)) {
  13939. $this->log(0, 'WARNING: configuration download directory "' . $downloaddir .
  13940. '" is not writeable. Change download_dir config variable to ' .
  13941. 'a writeable dir to avoid this warning');
  13942. }
  13943. if (!class_exists('System')) {
  13944. require_once 'phar://go-pear.phar/' . 'System.php';
  13945. }
  13946. if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
  13947. return $downloaddir;
  13948. }
  13949. $this->log(3, '+ tmp dir created at ' . $downloaddir);
  13950. }
  13951. if (!is_writable($downloaddir)) {
  13952. if (PEAR::isError(System::mkdir(array('-p', $downloaddir))) ||
  13953. !is_writable($downloaddir)) {
  13954. return PEAR::raiseError('download directory "' . $downloaddir .
  13955. '" is not writeable. Change download_dir config variable to ' .
  13956. 'a writeable dir');
  13957. }
  13958. }
  13959. return $this->_downloadDir = $downloaddir;
  13960. }
  13961. function setDownloadDir($dir)
  13962. {
  13963. if (!@is_writable($dir)) {
  13964. if (PEAR::isError(System::mkdir(array('-p', $dir)))) {
  13965. return PEAR::raiseError('download directory "' . $dir .
  13966. '" is not writeable. Change download_dir config variable to ' .
  13967. 'a writeable dir');
  13968. }
  13969. }
  13970. $this->_downloadDir = $dir;
  13971. }
  13972. function configSet($key, $value, $layer = 'user', $channel = false)
  13973. {
  13974. $this->config->set($key, $value, $layer, $channel);
  13975. $this->_preferredState = $this->config->get('preferred_state', null, $channel);
  13976. if (!$this->_preferredState) {
  13977. // don't inadvertently use a non-set preferred_state
  13978. $this->_preferredState = null;
  13979. }
  13980. }
  13981. function setOptions($options)
  13982. {
  13983. $this->_options = $options;
  13984. }
  13985. function getOptions()
  13986. {
  13987. return $this->_options;
  13988. }
  13989. /**
  13990. * @param array output of {@link parsePackageName()}
  13991. * @access private
  13992. */
  13993. function _getPackageDownloadUrl($parr)
  13994. {
  13995. $curchannel = $this->config->get('default_channel');
  13996. $this->configSet('default_channel', $parr['channel']);
  13997. // getDownloadURL returns an array. On error, it only contains information
  13998. // on the latest release as array(version, info). On success it contains
  13999. // array(version, info, download url string)
  14000. $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
  14001. if (!$this->_registry->channelExists($parr['channel'])) {
  14002. do {
  14003. if ($this->config->get('auto_discover') && $this->discover($parr['channel'])) {
  14004. break;
  14005. }
  14006. $this->configSet('default_channel', $curchannel);
  14007. return PEAR::raiseError('Unknown remote channel: ' . $parr['channel']);
  14008. } while (false);
  14009. }
  14010. $chan = $this->_registry->getChannel($parr['channel']);
  14011. if (PEAR::isError($chan)) {
  14012. return $chan;
  14013. }
  14014. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  14015. $version = $this->_registry->packageInfo($parr['package'], 'version', $parr['channel']);
  14016. $stability = $this->_registry->packageInfo($parr['package'], 'stability', $parr['channel']);
  14017. // package is installed - use the installed release stability level
  14018. if (!isset($parr['state']) && $stability !== null) {
  14019. $state = $stability['release'];
  14020. }
  14021. PEAR::staticPopErrorHandling();
  14022. $base2 = false;
  14023. $preferred_mirror = $this->config->get('preferred_mirror');
  14024. if (!$chan->supportsREST($preferred_mirror) ||
  14025. (
  14026. !($base2 = $chan->getBaseURL('REST1.3', $preferred_mirror))
  14027. &&
  14028. !($base = $chan->getBaseURL('REST1.0', $preferred_mirror))
  14029. )
  14030. ) {
  14031. return $this->raiseError($parr['channel'] . ' is using an unsupported protocol - This should never happen. Use --force to continue');
  14032. }
  14033. if ($base2) {
  14034. $rest = &$this->config->getREST('1.3', $this->_options);
  14035. $base = $base2;
  14036. } else {
  14037. $rest = &$this->config->getREST('1.0', $this->_options);
  14038. }
  14039. $downloadVersion = false;
  14040. if (!isset($parr['version']) && !isset($parr['state']) && $version
  14041. && !PEAR::isError($version)
  14042. && !isset($this->_options['downloadonly'])
  14043. ) {
  14044. $downloadVersion = $version;
  14045. }
  14046. $url = $rest->getDownloadURL($base, $parr, $state, $downloadVersion, $chan->getName());
  14047. if (PEAR::isError($url)) {
  14048. $this->configSet('default_channel', $curchannel);
  14049. return $url;
  14050. }
  14051. if ($parr['channel'] != $curchannel) {
  14052. $this->configSet('default_channel', $curchannel);
  14053. }
  14054. if (!is_array($url)) {
  14055. return $url;
  14056. }
  14057. $url['raw'] = false; // no checking is necessary for REST
  14058. if (!is_array($url['info'])) {
  14059. return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
  14060. 'this should never happen');
  14061. }
  14062. if (!isset($this->_options['force']) &&
  14063. !isset($this->_options['downloadonly']) &&
  14064. $version &&
  14065. !PEAR::isError($version) &&
  14066. !isset($parr['group'])
  14067. ) {
  14068. if (version_compare($version, $url['version'], '=')) {
  14069. return PEAR::raiseError($this->_registry->parsedPackageNameToString(
  14070. $parr, true) . ' is already installed and is the same as the ' .
  14071. 'released version ' . $url['version'], -976);
  14072. }
  14073. if (version_compare($version, $url['version'], '>')) {
  14074. return PEAR::raiseError($this->_registry->parsedPackageNameToString(
  14075. $parr, true) . ' is already installed and is newer than detected ' .
  14076. 'released version ' . $url['version'], -976);
  14077. }
  14078. }
  14079. if (isset($url['info']['required']) || $url['compatible']) {
  14080. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/v2.php';
  14081. $pf = new PEAR_PackageFile_v2;
  14082. $pf->setRawChannel($parr['channel']);
  14083. if ($url['compatible']) {
  14084. $pf->setRawCompatible($url['compatible']);
  14085. }
  14086. } else {
  14087. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/v1.php';
  14088. $pf = new PEAR_PackageFile_v1;
  14089. }
  14090. $pf->setRawPackage($url['package']);
  14091. $pf->setDeps($url['info']);
  14092. if ($url['compatible']) {
  14093. $pf->setCompatible($url['compatible']);
  14094. }
  14095. $pf->setRawState($url['stability']);
  14096. $url['info'] = &$pf;
  14097. if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
  14098. $ext = '.tar';
  14099. } else {
  14100. $ext = '.tgz';
  14101. }
  14102. if (is_array($url) && isset($url['url'])) {
  14103. $url['url'] .= $ext;
  14104. }
  14105. return $url;
  14106. }
  14107. /**
  14108. * @param array dependency array
  14109. * @access private
  14110. */
  14111. function _getDepPackageDownloadUrl($dep, $parr)
  14112. {
  14113. $xsdversion = isset($dep['rel']) ? '1.0' : '2.0';
  14114. $curchannel = $this->config->get('default_channel');
  14115. if (isset($dep['uri'])) {
  14116. $xsdversion = '2.0';
  14117. $chan = $this->_registry->getChannel('__uri');
  14118. if (PEAR::isError($chan)) {
  14119. return $chan;
  14120. }
  14121. $version = $this->_registry->packageInfo($dep['name'], 'version', '__uri');
  14122. $this->configSet('default_channel', '__uri');
  14123. } else {
  14124. if (isset($dep['channel'])) {
  14125. $remotechannel = $dep['channel'];
  14126. } else {
  14127. $remotechannel = 'pear.php.net';
  14128. }
  14129. if (!$this->_registry->channelExists($remotechannel)) {
  14130. do {
  14131. if ($this->config->get('auto_discover')) {
  14132. if ($this->discover($remotechannel)) {
  14133. break;
  14134. }
  14135. }
  14136. return PEAR::raiseError('Unknown remote channel: ' . $remotechannel);
  14137. } while (false);
  14138. }
  14139. $chan = $this->_registry->getChannel($remotechannel);
  14140. if (PEAR::isError($chan)) {
  14141. return $chan;
  14142. }
  14143. $version = $this->_registry->packageInfo($dep['name'], 'version', $remotechannel);
  14144. $this->configSet('default_channel', $remotechannel);
  14145. }
  14146. $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
  14147. if (isset($parr['state']) && isset($parr['version'])) {
  14148. unset($parr['state']);
  14149. }
  14150. if (isset($dep['uri'])) {
  14151. $info = $this->newDownloaderPackage($this);
  14152. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  14153. $err = $info->initialize($dep);
  14154. PEAR::staticPopErrorHandling();
  14155. if (!$err) {
  14156. // skip parameters that were missed by preferred_state
  14157. return PEAR::raiseError('Cannot initialize dependency');
  14158. }
  14159. if (PEAR::isError($err)) {
  14160. if (!isset($this->_options['soft'])) {
  14161. $this->log(0, $err->getMessage());
  14162. }
  14163. if (is_object($info)) {
  14164. $param = $info->getChannel() . '/' . $info->getPackage();
  14165. }
  14166. return PEAR::raiseError('Package "' . $param . '" is not valid');
  14167. }
  14168. return $info;
  14169. } elseif ($chan->supportsREST($this->config->get('preferred_mirror'))
  14170. &&
  14171. (
  14172. ($base2 = $chan->getBaseURL('REST1.3', $this->config->get('preferred_mirror')))
  14173. ||
  14174. ($base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror')))
  14175. )
  14176. ) {
  14177. if ($base2) {
  14178. $base = $base2;
  14179. $rest = &$this->config->getREST('1.3', $this->_options);
  14180. } else {
  14181. $rest = &$this->config->getREST('1.0', $this->_options);
  14182. }
  14183. $url = $rest->getDepDownloadURL($base, $xsdversion, $dep, $parr,
  14184. $state, $version, $chan->getName());
  14185. if (PEAR::isError($url)) {
  14186. return $url;
  14187. }
  14188. if ($parr['channel'] != $curchannel) {
  14189. $this->configSet('default_channel', $curchannel);
  14190. }
  14191. if (!is_array($url)) {
  14192. return $url;
  14193. }
  14194. $url['raw'] = false; // no checking is necessary for REST
  14195. if (!is_array($url['info'])) {
  14196. return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
  14197. 'this should never happen');
  14198. }
  14199. if (isset($url['info']['required'])) {
  14200. if (!class_exists('PEAR_PackageFile_v2')) {
  14201. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/v2.php';
  14202. }
  14203. $pf = new PEAR_PackageFile_v2;
  14204. $pf->setRawChannel($remotechannel);
  14205. } else {
  14206. if (!class_exists('PEAR_PackageFile_v1')) {
  14207. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/v1.php';
  14208. }
  14209. $pf = new PEAR_PackageFile_v1;
  14210. }
  14211. $pf->setRawPackage($url['package']);
  14212. $pf->setDeps($url['info']);
  14213. if ($url['compatible']) {
  14214. $pf->setCompatible($url['compatible']);
  14215. }
  14216. $pf->setRawState($url['stability']);
  14217. $url['info'] = &$pf;
  14218. if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
  14219. $ext = '.tar';
  14220. } else {
  14221. $ext = '.tgz';
  14222. }
  14223. if (is_array($url) && isset($url['url'])) {
  14224. $url['url'] .= $ext;
  14225. }
  14226. return $url;
  14227. }
  14228. return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.');
  14229. }
  14230. /**
  14231. * @deprecated in favor of _getPackageDownloadUrl
  14232. */
  14233. function getPackageDownloadUrl($package, $version = null, $channel = false)
  14234. {
  14235. if ($version) {
  14236. $package .= "-$version";
  14237. }
  14238. if ($this === null || $this->_registry === null) {
  14239. $package = "http://pear.php.net/get/$package";
  14240. } else {
  14241. $chan = $this->_registry->getChannel($channel);
  14242. if (PEAR::isError($chan)) {
  14243. return '';
  14244. }
  14245. $package = "http://" . $chan->getServer() . "/get/$package";
  14246. }
  14247. if (!extension_loaded("zlib")) {
  14248. $package .= '?uncompress=yes';
  14249. }
  14250. return $package;
  14251. }
  14252. /**
  14253. * Retrieve a list of downloaded packages after a call to {@link download()}.
  14254. *
  14255. * Also resets the list of downloaded packages.
  14256. * @return array
  14257. */
  14258. function getDownloadedPackages()
  14259. {
  14260. $ret = $this->_downloadedPackages;
  14261. $this->_downloadedPackages = array();
  14262. $this->_toDownload = array();
  14263. return $ret;
  14264. }
  14265. function _downloadCallback($msg, $params = null)
  14266. {
  14267. switch ($msg) {
  14268. case 'saveas':
  14269. $this->log(1, "downloading $params ...");
  14270. break;
  14271. case 'done':
  14272. $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes');
  14273. break;
  14274. case 'bytesread':
  14275. static $bytes;
  14276. if (empty($bytes)) {
  14277. $bytes = 0;
  14278. }
  14279. if (!($bytes % 10240)) {
  14280. $this->log(1, '.', false);
  14281. }
  14282. $bytes += $params;
  14283. break;
  14284. case 'start':
  14285. if($params[1] == -1) {
  14286. $length = "Unknown size";
  14287. } else {
  14288. $length = number_format($params[1], 0, '', ',')." bytes";
  14289. }
  14290. $this->log(1, "Starting to download {$params[0]} ($length)");
  14291. break;
  14292. }
  14293. if (method_exists($this->ui, '_downloadCallback'))
  14294. $this->ui->_downloadCallback($msg, $params);
  14295. }
  14296. function _prependPath($path, $prepend)
  14297. {
  14298. if (strlen($prepend) > 0) {
  14299. if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
  14300. if (preg_match('/^[a-z]:/i', $prepend)) {
  14301. $prepend = substr($prepend, 2);
  14302. } elseif ($prepend[0] != '\\') {
  14303. $prepend = "\\$prepend";
  14304. }
  14305. $path = substr($path, 0, 2) . $prepend . substr($path, 2);
  14306. } else {
  14307. $path = $prepend . $path;
  14308. }
  14309. }
  14310. return $path;
  14311. }
  14312. /**
  14313. * @param string
  14314. * @param integer
  14315. */
  14316. function pushError($errmsg, $code = -1)
  14317. {
  14318. array_push($this->_errorStack, array($errmsg, $code));
  14319. }
  14320. function getErrorMsgs()
  14321. {
  14322. $msgs = array();
  14323. $errs = $this->_errorStack;
  14324. foreach ($errs as $err) {
  14325. $msgs[] = $err[0];
  14326. }
  14327. $this->_errorStack = array();
  14328. return $msgs;
  14329. }
  14330. /**
  14331. * for BC
  14332. *
  14333. * @deprecated
  14334. */
  14335. function sortPkgDeps(&$packages, $uninstall = false)
  14336. {
  14337. $uninstall ?
  14338. $this->sortPackagesForUninstall($packages) :
  14339. $this->sortPackagesForInstall($packages);
  14340. }
  14341. /**
  14342. * Sort a list of arrays of array(downloaded packagefilename) by dependency.
  14343. *
  14344. * This uses the topological sort method from graph theory, and the
  14345. * Structures_Graph package to properly sort dependencies for installation.
  14346. * @param array an array of downloaded PEAR_Downloader_Packages
  14347. * @return array array of array(packagefilename, package.xml contents)
  14348. */
  14349. function sortPackagesForInstall(&$packages)
  14350. {
  14351. require_once 'phar://go-pear.phar/' . 'Structures/Graph.php';
  14352. require_once 'phar://go-pear.phar/' . 'Structures/Graph/Node.php';
  14353. require_once 'phar://go-pear.phar/' . 'Structures/Graph/Manipulator/TopologicalSorter.php';
  14354. $depgraph = new Structures_Graph(true);
  14355. $nodes = array();
  14356. $reg = &$this->config->getRegistry();
  14357. foreach ($packages as $i => $package) {
  14358. $pname = $reg->parsedPackageNameToString(
  14359. array(
  14360. 'channel' => $package->getChannel(),
  14361. 'package' => strtolower($package->getPackage()),
  14362. ));
  14363. $nodes[$pname] = new Structures_Graph_Node;
  14364. $nodes[$pname]->setData($packages[$i]);
  14365. $depgraph->addNode($nodes[$pname]);
  14366. }
  14367. $deplinks = array();
  14368. foreach ($nodes as $package => $node) {
  14369. $pf = &$node->getData();
  14370. $pdeps = $pf->getDeps(true);
  14371. if (!$pdeps) {
  14372. continue;
  14373. }
  14374. if ($pf->getPackagexmlVersion() == '1.0') {
  14375. foreach ($pdeps as $dep) {
  14376. if ($dep['type'] != 'pkg' ||
  14377. (isset($dep['optional']) && $dep['optional'] == 'yes')) {
  14378. continue;
  14379. }
  14380. $dname = $reg->parsedPackageNameToString(
  14381. array(
  14382. 'channel' => 'pear.php.net',
  14383. 'package' => strtolower($dep['name']),
  14384. ));
  14385. if (isset($nodes[$dname])) {
  14386. if (!isset($deplinks[$dname])) {
  14387. $deplinks[$dname] = array();
  14388. }
  14389. $deplinks[$dname][$package] = 1;
  14390. // dependency is in installed packages
  14391. continue;
  14392. }
  14393. $dname = $reg->parsedPackageNameToString(
  14394. array(
  14395. 'channel' => 'pecl.php.net',
  14396. 'package' => strtolower($dep['name']),
  14397. ));
  14398. if (isset($nodes[$dname])) {
  14399. if (!isset($deplinks[$dname])) {
  14400. $deplinks[$dname] = array();
  14401. }
  14402. $deplinks[$dname][$package] = 1;
  14403. // dependency is in installed packages
  14404. continue;
  14405. }
  14406. }
  14407. } else {
  14408. // the only ordering we care about is:
  14409. // 1) subpackages must be installed before packages that depend on them
  14410. // 2) required deps must be installed before packages that depend on them
  14411. if (isset($pdeps['required']['subpackage'])) {
  14412. $t = $pdeps['required']['subpackage'];
  14413. if (!isset($t[0])) {
  14414. $t = array($t);
  14415. }
  14416. $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
  14417. }
  14418. if (isset($pdeps['group'])) {
  14419. if (!isset($pdeps['group'][0])) {
  14420. $pdeps['group'] = array($pdeps['group']);
  14421. }
  14422. foreach ($pdeps['group'] as $group) {
  14423. if (isset($group['subpackage'])) {
  14424. $t = $group['subpackage'];
  14425. if (!isset($t[0])) {
  14426. $t = array($t);
  14427. }
  14428. $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
  14429. }
  14430. }
  14431. }
  14432. if (isset($pdeps['optional']['subpackage'])) {
  14433. $t = $pdeps['optional']['subpackage'];
  14434. if (!isset($t[0])) {
  14435. $t = array($t);
  14436. }
  14437. $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
  14438. }
  14439. if (isset($pdeps['required']['package'])) {
  14440. $t = $pdeps['required']['package'];
  14441. if (!isset($t[0])) {
  14442. $t = array($t);
  14443. }
  14444. $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
  14445. }
  14446. if (isset($pdeps['group'])) {
  14447. if (!isset($pdeps['group'][0])) {
  14448. $pdeps['group'] = array($pdeps['group']);
  14449. }
  14450. foreach ($pdeps['group'] as $group) {
  14451. if (isset($group['package'])) {
  14452. $t = $group['package'];
  14453. if (!isset($t[0])) {
  14454. $t = array($t);
  14455. }
  14456. $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
  14457. }
  14458. }
  14459. }
  14460. }
  14461. }
  14462. $this->_detectDepCycle($deplinks);
  14463. foreach ($deplinks as $dependent => $parents) {
  14464. foreach ($parents as $parent => $unused) {
  14465. $nodes[$dependent]->connectTo($nodes[$parent]);
  14466. }
  14467. }
  14468. $installOrder = Structures_Graph_Manipulator_TopologicalSorter::sort($depgraph);
  14469. $ret = array();
  14470. for ($i = 0, $count = count($installOrder); $i < $count; $i++) {
  14471. foreach ($installOrder[$i] as $index => $sortedpackage) {
  14472. $data = &$installOrder[$i][$index]->getData();
  14473. $ret[] = &$nodes[$reg->parsedPackageNameToString(
  14474. array(
  14475. 'channel' => $data->getChannel(),
  14476. 'package' => strtolower($data->getPackage()),
  14477. ))]->getData();
  14478. }
  14479. }
  14480. $packages = $ret;
  14481. return;
  14482. }
  14483. /**
  14484. * Detect recursive links between dependencies and break the cycles
  14485. *
  14486. * @param array
  14487. * @access private
  14488. */
  14489. function _detectDepCycle(&$deplinks)
  14490. {
  14491. do {
  14492. $keepgoing = false;
  14493. foreach ($deplinks as $dep => $parents) {
  14494. foreach ($parents as $parent => $unused) {
  14495. // reset the parent cycle detector
  14496. $this->_testCycle(null, null, null);
  14497. if ($this->_testCycle($dep, $deplinks, $parent)) {
  14498. $keepgoing = true;
  14499. unset($deplinks[$dep][$parent]);
  14500. if (count($deplinks[$dep]) == 0) {
  14501. unset($deplinks[$dep]);
  14502. }
  14503. continue 3;
  14504. }
  14505. }
  14506. }
  14507. } while ($keepgoing);
  14508. }
  14509. function _testCycle($test, $deplinks, $dep)
  14510. {
  14511. static $visited = array();
  14512. if ($test === null) {
  14513. $visited = array();
  14514. return;
  14515. }
  14516. // this happens when a parent has a dep cycle on another dependency
  14517. // but the child is not part of the cycle
  14518. if (isset($visited[$dep])) {
  14519. return false;
  14520. }
  14521. $visited[$dep] = 1;
  14522. if ($test == $dep) {
  14523. return true;
  14524. }
  14525. if (isset($deplinks[$dep])) {
  14526. if (in_array($test, array_keys($deplinks[$dep]), true)) {
  14527. return true;
  14528. }
  14529. foreach ($deplinks[$dep] as $parent => $unused) {
  14530. if ($this->_testCycle($test, $deplinks, $parent)) {
  14531. return true;
  14532. }
  14533. }
  14534. }
  14535. return false;
  14536. }
  14537. /**
  14538. * Set up the dependency for installation parsing
  14539. *
  14540. * @param array $t dependency information
  14541. * @param PEAR_Registry $reg
  14542. * @param array $deplinks list of dependency links already established
  14543. * @param array $nodes all existing package nodes
  14544. * @param string $package parent package name
  14545. * @access private
  14546. */
  14547. function _setupGraph($t, $reg, &$deplinks, &$nodes, $package)
  14548. {
  14549. foreach ($t as $dep) {
  14550. $depchannel = !isset($dep['channel']) ? '__uri': $dep['channel'];
  14551. $dname = $reg->parsedPackageNameToString(
  14552. array(
  14553. 'channel' => $depchannel,
  14554. 'package' => strtolower($dep['name']),
  14555. ));
  14556. if (isset($nodes[$dname])) {
  14557. if (!isset($deplinks[$dname])) {
  14558. $deplinks[$dname] = array();
  14559. }
  14560. $deplinks[$dname][$package] = 1;
  14561. }
  14562. }
  14563. }
  14564. function _dependsOn($a, $b)
  14565. {
  14566. return $this->_checkDepTree(strtolower($a->getChannel()), strtolower($a->getPackage()), $b);
  14567. }
  14568. function _checkDepTree($channel, $package, $b, $checked = array())
  14569. {
  14570. $checked[$channel][$package] = true;
  14571. if (!isset($this->_depTree[$channel][$package])) {
  14572. return false;
  14573. }
  14574. if (isset($this->_depTree[$channel][$package][strtolower($b->getChannel())]
  14575. [strtolower($b->getPackage())])) {
  14576. return true;
  14577. }
  14578. foreach ($this->_depTree[$channel][$package] as $ch => $packages) {
  14579. foreach ($packages as $pa => $true) {
  14580. if ($this->_checkDepTree($ch, $pa, $b, $checked)) {
  14581. return true;
  14582. }
  14583. }
  14584. }
  14585. return false;
  14586. }
  14587. function _sortInstall($a, $b)
  14588. {
  14589. if (!$a->getDeps() && !$b->getDeps()) {
  14590. return 0; // neither package has dependencies, order is insignificant
  14591. }
  14592. if ($a->getDeps() && !$b->getDeps()) {
  14593. return 1; // $a must be installed after $b because $a has dependencies
  14594. }
  14595. if (!$a->getDeps() && $b->getDeps()) {
  14596. return -1; // $b must be installed after $a because $b has dependencies
  14597. }
  14598. // both packages have dependencies
  14599. if ($this->_dependsOn($a, $b)) {
  14600. return 1;
  14601. }
  14602. if ($this->_dependsOn($b, $a)) {
  14603. return -1;
  14604. }
  14605. return 0;
  14606. }
  14607. /**
  14608. * Download a file through HTTP. Considers suggested file name in
  14609. * Content-disposition: header and can run a callback function for
  14610. * different events. The callback will be called with two
  14611. * parameters: the callback type, and parameters. The implemented
  14612. * callback types are:
  14613. *
  14614. * 'setup' called at the very beginning, parameter is a UI object
  14615. * that should be used for all output
  14616. * 'message' the parameter is a string with an informational message
  14617. * 'saveas' may be used to save with a different file name, the
  14618. * parameter is the filename that is about to be used.
  14619. * If a 'saveas' callback returns a non-empty string,
  14620. * that file name will be used as the filename instead.
  14621. * Note that $save_dir will not be affected by this, only
  14622. * the basename of the file.
  14623. * 'start' download is starting, parameter is number of bytes
  14624. * that are expected, or -1 if unknown
  14625. * 'bytesread' parameter is the number of bytes read so far
  14626. * 'done' download is complete, parameter is the total number
  14627. * of bytes read
  14628. * 'connfailed' if the TCP/SSL connection fails, this callback is called
  14629. * with array(host,port,errno,errmsg)
  14630. * 'writefailed' if writing to disk fails, this callback is called
  14631. * with array(destfile,errmsg)
  14632. *
  14633. * If an HTTP proxy has been configured (http_proxy PEAR_Config
  14634. * setting), the proxy will be used.
  14635. *
  14636. * @param string $url the URL to download
  14637. * @param object $ui PEAR_Frontend_* instance
  14638. * @param object $config PEAR_Config instance
  14639. * @param string $save_dir directory to save file in
  14640. * @param mixed $callback function/method to call for status
  14641. * updates
  14642. * @param false|string|array $lastmodified header values to check against for caching
  14643. * use false to return the header values from this download
  14644. * @param false|array $accept Accept headers to send
  14645. * @param false|string $channel Channel to use for retrieving authentication
  14646. * @return mixed Returns the full path of the downloaded file or a PEAR
  14647. * error on failure. If the error is caused by
  14648. * socket-related errors, the error object will
  14649. * have the fsockopen error code available through
  14650. * getCode(). If caching is requested, then return the header
  14651. * values.
  14652. * If $lastmodified was given and the there are no changes,
  14653. * boolean false is returned.
  14654. *
  14655. * @access public
  14656. */
  14657. public static function _downloadHttp(
  14658. $object, $url, &$ui, $save_dir = '.', $callback = null, $lastmodified = null,
  14659. $accept = false, $channel = false
  14660. ) {
  14661. static $redirect = 0;
  14662. // always reset , so we are clean case of error
  14663. $wasredirect = $redirect;
  14664. $redirect = 0;
  14665. if ($callback) {
  14666. call_user_func($callback, 'setup', array(&$ui));
  14667. }
  14668. $info = parse_url($url);
  14669. if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) {
  14670. return PEAR::raiseError('Cannot download non-http URL "' . $url . '"');
  14671. }
  14672. if (!isset($info['host'])) {
  14673. return PEAR::raiseError('Cannot download from non-URL "' . $url . '"');
  14674. }
  14675. $host = isset($info['host']) ? $info['host'] : null;
  14676. $port = isset($info['port']) ? $info['port'] : null;
  14677. $path = isset($info['path']) ? $info['path'] : null;
  14678. if ($object !== null) {
  14679. $config = $object->config;
  14680. } else {
  14681. $config = &PEAR_Config::singleton();
  14682. }
  14683. $proxy = new PEAR_Proxy($config);
  14684. if ($proxy->isProxyConfigured() && $callback) {
  14685. call_user_func($callback, 'message', "Using HTTP proxy $host:$port");
  14686. }
  14687. if (empty($port)) {
  14688. $port = (isset($info['scheme']) && $info['scheme'] == 'https') ? 443 : 80;
  14689. }
  14690. $scheme = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http';
  14691. $secure = ($scheme == 'https');
  14692. $fp = $proxy->openSocket($host, $port, $secure);
  14693. if (PEAR::isError($fp)) {
  14694. if ($callback) {
  14695. $errno = $fp->getCode();
  14696. $errstr = $fp->getMessage();
  14697. call_user_func($callback, 'connfailed', array($host, $port,
  14698. $errno, $errstr));
  14699. }
  14700. return $fp;
  14701. }
  14702. $requestPath = $path;
  14703. if ($proxy->isProxyConfigured()) {
  14704. $requestPath = $url;
  14705. }
  14706. if ($lastmodified === false || $lastmodified) {
  14707. $request = "GET $requestPath HTTP/1.1\r\n";
  14708. } else {
  14709. $request = "GET $requestPath HTTP/1.0\r\n";
  14710. }
  14711. $request .= "Host: $host\r\n";
  14712. $ifmodifiedsince = '';
  14713. if (is_array($lastmodified)) {
  14714. if (isset($lastmodified['Last-Modified'])) {
  14715. $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n";
  14716. }
  14717. if (isset($lastmodified['ETag'])) {
  14718. $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";
  14719. }
  14720. } else {
  14721. $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');
  14722. }
  14723. $request .= $ifmodifiedsince .
  14724. "User-Agent: PEAR/1.10.16/PHP/" . PHP_VERSION . "\r\n";
  14725. if ($object !== null) { // only pass in authentication for non-static calls
  14726. $username = $config->get('username', null, $channel);
  14727. $password = $config->get('password', null, $channel);
  14728. if ($username && $password) {
  14729. $tmp = base64_encode("$username:$password");
  14730. $request .= "Authorization: Basic $tmp\r\n";
  14731. }
  14732. }
  14733. $proxyAuth = $proxy->getProxyAuth();
  14734. if ($proxyAuth) {
  14735. $request .= 'Proxy-Authorization: Basic ' .
  14736. $proxyAuth . "\r\n";
  14737. }
  14738. if ($accept) {
  14739. $request .= 'Accept: ' . implode(', ', $accept) . "\r\n";
  14740. }
  14741. $request .= "Connection: close\r\n";
  14742. $request .= "\r\n";
  14743. fwrite($fp, $request);
  14744. $headers = array();
  14745. $reply = 0;
  14746. while (trim($line = fgets($fp, 1024))) {
  14747. if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) {
  14748. $headers[strtolower($matches[1])] = trim($matches[2]);
  14749. } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
  14750. $reply = (int)$matches[1];
  14751. if ($reply == 304 && ($lastmodified || ($lastmodified === false))) {
  14752. return false;
  14753. }
  14754. if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) {
  14755. return PEAR::raiseError("File $scheme://$host:$port$path not valid (received: $line)");
  14756. }
  14757. }
  14758. }
  14759. if ($reply != 200) {
  14760. if (!isset($headers['location'])) {
  14761. return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirected but no location)");
  14762. }
  14763. if ($wasredirect > 4) {
  14764. return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirection looped more than 5 times)");
  14765. }
  14766. $redirect = $wasredirect + 1;
  14767. return static::_downloadHttp($object, $headers['location'],
  14768. $ui, $save_dir, $callback, $lastmodified, $accept);
  14769. }
  14770. if (isset($headers['content-disposition']) &&
  14771. preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|\\z)/', $headers['content-disposition'], $matches)) {
  14772. $save_as = basename($matches[1]);
  14773. } else {
  14774. $save_as = basename($url);
  14775. }
  14776. if ($callback) {
  14777. $tmp = call_user_func($callback, 'saveas', $save_as);
  14778. if ($tmp) {
  14779. $save_as = $tmp;
  14780. }
  14781. }
  14782. $dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as;
  14783. if (is_link($dest_file)) {
  14784. return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $dest_file . ' as it is symlinked to ' . readlink($dest_file) . ' - Possible symlink attack');
  14785. }
  14786. if (!$wp = @fopen($dest_file, 'wb')) {
  14787. fclose($fp);
  14788. if ($callback) {
  14789. call_user_func($callback, 'writefailed',
  14790. array($dest_file, error_get_last()["message"]));
  14791. }
  14792. return PEAR::raiseError("could not open $dest_file for writing");
  14793. }
  14794. $length = isset($headers['content-length']) ? $headers['content-length'] : -1;
  14795. $bytes = 0;
  14796. if ($callback) {
  14797. call_user_func($callback, 'start', array(basename($dest_file), $length));
  14798. }
  14799. while ($data = fread($fp, 1024)) {
  14800. $bytes += strlen($data);
  14801. if ($callback) {
  14802. call_user_func($callback, 'bytesread', $bytes);
  14803. }
  14804. if (!@fwrite($wp, $data)) {
  14805. fclose($fp);
  14806. if ($callback) {
  14807. call_user_func($callback, 'writefailed',
  14808. array($dest_file, error_get_last()["message"]));
  14809. }
  14810. return PEAR::raiseError(
  14811. "$dest_file: write failed (" . error_get_last()["message"] . ")");
  14812. }
  14813. }
  14814. fclose($fp);
  14815. fclose($wp);
  14816. if ($callback) {
  14817. call_user_func($callback, 'done', $bytes);
  14818. }
  14819. if ($lastmodified === false || $lastmodified) {
  14820. if (isset($headers['etag'])) {
  14821. $lastmodified = array('ETag' => $headers['etag']);
  14822. }
  14823. if (isset($headers['last-modified'])) {
  14824. if (is_array($lastmodified)) {
  14825. $lastmodified['Last-Modified'] = $headers['last-modified'];
  14826. } else {
  14827. $lastmodified = $headers['last-modified'];
  14828. }
  14829. }
  14830. return array($dest_file, $lastmodified, $headers);
  14831. }
  14832. return $dest_file;
  14833. }
  14834. }
  14835. <?php
  14836. /**
  14837. * PEAR_Downloader_Package
  14838. *
  14839. * PHP versions 4 and 5
  14840. *
  14841. * @category pear
  14842. * @package PEAR
  14843. * @author Greg Beaver <cellog@php.net>
  14844. * @copyright 1997-2009 The Authors
  14845. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  14846. * @link http://pear.php.net/package/PEAR
  14847. * @since File available since Release 1.4.0a1
  14848. */
  14849. /**
  14850. * Error code when parameter initialization fails because no releases
  14851. * exist within preferred_state, but releases do exist
  14852. */
  14853. define('PEAR_DOWNLOADER_PACKAGE_STATE', -1003);
  14854. /**
  14855. * Error code when parameter initialization fails because no releases
  14856. * exist that will work with the existing PHP version
  14857. */
  14858. define('PEAR_DOWNLOADER_PACKAGE_PHPVERSION', -1004);
  14859. /**
  14860. * Coordinates download parameters and manages their dependencies
  14861. * prior to downloading them.
  14862. *
  14863. * Input can come from three sources:
  14864. *
  14865. * - local files (archives or package.xml)
  14866. * - remote files (downloadable urls)
  14867. * - abstract package names
  14868. *
  14869. * The first two elements are handled cleanly by PEAR_PackageFile, but the third requires
  14870. * accessing pearweb's xml-rpc interface to determine necessary dependencies, and the
  14871. * format returned of dependencies is slightly different from that used in package.xml.
  14872. *
  14873. * This class hides the differences between these elements, and makes automatic
  14874. * dependency resolution a piece of cake. It also manages conflicts when
  14875. * two classes depend on incompatible dependencies, or differing versions of the same
  14876. * package dependency. In addition, download will not be attempted if the php version is
  14877. * not supported, PEAR installer version is not supported, or non-PECL extensions are not
  14878. * installed.
  14879. * @category pear
  14880. * @package PEAR
  14881. * @author Greg Beaver <cellog@php.net>
  14882. * @copyright 1997-2009 The Authors
  14883. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  14884. * @version Release: 1.10.16
  14885. * @link http://pear.php.net/package/PEAR
  14886. * @since Class available since Release 1.4.0a1
  14887. */
  14888. class PEAR_Downloader_Package
  14889. {
  14890. /**
  14891. * @var PEAR_Downloader
  14892. */
  14893. var $_downloader;
  14894. /**
  14895. * @var PEAR_Config
  14896. */
  14897. var $_config;
  14898. /**
  14899. * @var PEAR_Registry
  14900. */
  14901. var $_registry;
  14902. /**
  14903. * Used to implement packagingroot properly
  14904. * @var PEAR_Registry
  14905. */
  14906. var $_installRegistry;
  14907. /**
  14908. * @var PEAR_PackageFile_v1|PEAR_PackageFile|v2
  14909. */
  14910. var $_packagefile;
  14911. /**
  14912. * @var array
  14913. */
  14914. var $_parsedname;
  14915. /**
  14916. * @var array
  14917. */
  14918. var $_downloadURL;
  14919. /**
  14920. * @var array
  14921. */
  14922. var $_downloadDeps = array();
  14923. /**
  14924. * @var boolean
  14925. */
  14926. var $_valid = false;
  14927. /**
  14928. * @var boolean
  14929. */
  14930. var $_analyzed = false;
  14931. /**
  14932. * if this or a parent package was invoked with Package-state, this is set to the
  14933. * state variable.
  14934. *
  14935. * This allows temporary reassignment of preferred_state for a parent package and all of
  14936. * its dependencies.
  14937. * @var string|false
  14938. */
  14939. var $_explicitState = false;
  14940. /**
  14941. * If this package is invoked with Package#group, this variable will be true
  14942. */
  14943. var $_explicitGroup = false;
  14944. /**
  14945. * Package type local|url
  14946. * @var string
  14947. */
  14948. var $_type;
  14949. /**
  14950. * Contents of package.xml, if downloaded from a remote channel
  14951. * @var string|false
  14952. * @access private
  14953. */
  14954. var $_rawpackagefile;
  14955. /**
  14956. * @var boolean
  14957. * @access private
  14958. */
  14959. var $_validated = false;
  14960. /**
  14961. * @param PEAR_Downloader
  14962. */
  14963. function __construct(&$downloader)
  14964. {
  14965. $this->_downloader = &$downloader;
  14966. $this->_config = &$this->_downloader->config;
  14967. $this->_registry = &$this->_config->getRegistry();
  14968. $options = $downloader->getOptions();
  14969. if (isset($options['packagingroot'])) {
  14970. $this->_config->setInstallRoot($options['packagingroot']);
  14971. $this->_installRegistry = &$this->_config->getRegistry();
  14972. $this->_config->setInstallRoot(false);
  14973. } else {
  14974. $this->_installRegistry = &$this->_registry;
  14975. }
  14976. $this->_valid = $this->_analyzed = false;
  14977. }
  14978. /**
  14979. * Parse the input and determine whether this is a local file, a remote uri, or an
  14980. * abstract package name.
  14981. *
  14982. * This is the heart of the PEAR_Downloader_Package(), and is used in
  14983. * {@link PEAR_Downloader::download()}
  14984. * @param string
  14985. * @return bool|PEAR_Error
  14986. */
  14987. function initialize($param)
  14988. {
  14989. $origErr = $this->_fromFile($param);
  14990. if ($this->_valid) {
  14991. return true;
  14992. }
  14993. $options = $this->_downloader->getOptions();
  14994. if (isset($options['offline'])) {
  14995. if (PEAR::isError($origErr) && !isset($options['soft'])) {
  14996. foreach ($origErr->getUserInfo() as $userInfo) {
  14997. if (isset($userInfo['message'])) {
  14998. $this->_downloader->log(0, $userInfo['message']);
  14999. }
  15000. }
  15001. $this->_downloader->log(0, $origErr->getMessage());
  15002. }
  15003. return PEAR::raiseError('Cannot download non-local package "' . $param . '"');
  15004. }
  15005. $err = $this->_fromUrl($param);
  15006. if (PEAR::isError($err) || !$this->_valid) {
  15007. if ($this->_type == 'url') {
  15008. if (PEAR::isError($err) && !isset($options['soft'])) {
  15009. $this->_downloader->log(0, $err->getMessage());
  15010. }
  15011. return PEAR::raiseError("Invalid or missing remote package file");
  15012. }
  15013. $err = $this->_fromString($param);
  15014. if (PEAR::isError($err) || !$this->_valid) {
  15015. if (PEAR::isError($err) && $err->getCode() == PEAR_DOWNLOADER_PACKAGE_STATE) {
  15016. return false; // instruct the downloader to silently skip
  15017. }
  15018. if (isset($this->_type) && $this->_type == 'local' && PEAR::isError($origErr)) {
  15019. if (is_array($origErr->getUserInfo())) {
  15020. foreach ($origErr->getUserInfo() as $err) {
  15021. if (is_array($err)) {
  15022. $err = $err['message'];
  15023. }
  15024. if (!isset($options['soft'])) {
  15025. $this->_downloader->log(0, $err);
  15026. }
  15027. }
  15028. }
  15029. if (!isset($options['soft'])) {
  15030. $this->_downloader->log(0, $origErr->getMessage());
  15031. }
  15032. if (is_array($param)) {
  15033. $param = $this->_registry->parsedPackageNameToString($param, true);
  15034. }
  15035. if (!isset($options['soft'])) {
  15036. $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file");
  15037. }
  15038. // Passing no message back - already logged above
  15039. return PEAR::raiseError();
  15040. }
  15041. if (PEAR::isError($err) && !isset($options['soft'])) {
  15042. $this->_downloader->log(0, $err->getMessage());
  15043. }
  15044. if (is_array($param)) {
  15045. $param = $this->_registry->parsedPackageNameToString($param, true);
  15046. }
  15047. if (!isset($options['soft'])) {
  15048. $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file");
  15049. }
  15050. // Passing no message back - already logged above
  15051. return PEAR::raiseError();
  15052. }
  15053. }
  15054. return true;
  15055. }
  15056. /**
  15057. * Retrieve any non-local packages
  15058. * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|PEAR_Error
  15059. */
  15060. function &download()
  15061. {
  15062. if (isset($this->_packagefile)) {
  15063. return $this->_packagefile;
  15064. }
  15065. if (isset($this->_downloadURL['url'])) {
  15066. $this->_isvalid = false;
  15067. $info = $this->getParsedPackage();
  15068. foreach ($info as $i => $p) {
  15069. $info[$i] = strtolower($p);
  15070. }
  15071. $err = $this->_fromUrl($this->_downloadURL['url'],
  15072. $this->_registry->parsedPackageNameToString($this->_parsedname, true));
  15073. $newinfo = $this->getParsedPackage();
  15074. foreach ($newinfo as $i => $p) {
  15075. $newinfo[$i] = strtolower($p);
  15076. }
  15077. if ($info != $newinfo) {
  15078. do {
  15079. if ($info['channel'] == 'pecl.php.net' && $newinfo['channel'] == 'pear.php.net') {
  15080. $info['channel'] = 'pear.php.net';
  15081. if ($info == $newinfo) {
  15082. // skip the channel check if a pecl package says it's a PEAR package
  15083. break;
  15084. }
  15085. }
  15086. if ($info['channel'] == 'pear.php.net' && $newinfo['channel'] == 'pecl.php.net') {
  15087. $info['channel'] = 'pecl.php.net';
  15088. if ($info == $newinfo) {
  15089. // skip the channel check if a pecl package says it's a PEAR package
  15090. break;
  15091. }
  15092. }
  15093. return PEAR::raiseError('CRITICAL ERROR: We are ' .
  15094. $this->_registry->parsedPackageNameToString($info) . ', but the file ' .
  15095. 'downloaded claims to be ' .
  15096. $this->_registry->parsedPackageNameToString($this->getParsedPackage()));
  15097. } while (false);
  15098. }
  15099. if (PEAR::isError($err) || !$this->_valid) {
  15100. return $err;
  15101. }
  15102. }
  15103. $this->_type = 'local';
  15104. return $this->_packagefile;
  15105. }
  15106. function &getPackageFile()
  15107. {
  15108. return $this->_packagefile;
  15109. }
  15110. function &getDownloader()
  15111. {
  15112. return $this->_downloader;
  15113. }
  15114. function getType()
  15115. {
  15116. return $this->_type;
  15117. }
  15118. /**
  15119. * Like {@link initialize()}, but operates on a dependency
  15120. */
  15121. function fromDepURL($dep)
  15122. {
  15123. $this->_downloadURL = $dep;
  15124. if (isset($dep['uri'])) {
  15125. $options = $this->_downloader->getOptions();
  15126. if (!extension_loaded("zlib") || isset($options['nocompress'])) {
  15127. $ext = '.tar';
  15128. } else {
  15129. $ext = '.tgz';
  15130. }
  15131. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  15132. $err = $this->_fromUrl($dep['uri'] . $ext);
  15133. PEAR::popErrorHandling();
  15134. if (PEAR::isError($err)) {
  15135. if (!isset($options['soft'])) {
  15136. $this->_downloader->log(0, $err->getMessage());
  15137. }
  15138. return PEAR::raiseError('Invalid uri dependency "' . $dep['uri'] . $ext . '", ' .
  15139. 'cannot download');
  15140. }
  15141. } else {
  15142. $this->_parsedname =
  15143. array(
  15144. 'package' => $dep['info']->getPackage(),
  15145. 'channel' => $dep['info']->getChannel(),
  15146. 'version' => $dep['version']
  15147. );
  15148. if (!isset($dep['nodefault'])) {
  15149. $this->_parsedname['group'] = 'default'; // download the default dependency group
  15150. $this->_explicitGroup = false;
  15151. }
  15152. $this->_rawpackagefile = $dep['raw'];
  15153. }
  15154. }
  15155. function detectDependencies($params)
  15156. {
  15157. $options = $this->_downloader->getOptions();
  15158. if (isset($options['downloadonly'])) {
  15159. return;
  15160. }
  15161. if (isset($options['offline'])) {
  15162. $this->_downloader->log(3, 'Skipping dependency download check, --offline specified');
  15163. return;
  15164. }
  15165. $pname = $this->getParsedPackage();
  15166. if (!$pname) {
  15167. return;
  15168. }
  15169. $deps = $this->getDeps();
  15170. if (!$deps) {
  15171. return;
  15172. }
  15173. if (isset($deps['required'])) { // package.xml 2.0
  15174. return $this->_detect2($deps, $pname, $options, $params);
  15175. }
  15176. return $this->_detect1($deps, $pname, $options, $params);
  15177. }
  15178. function setValidated()
  15179. {
  15180. $this->_validated = true;
  15181. }
  15182. function alreadyValidated()
  15183. {
  15184. return $this->_validated;
  15185. }
  15186. /**
  15187. * Remove packages to be downloaded that are already installed
  15188. * @param array of PEAR_Downloader_Package objects
  15189. */
  15190. public static function removeInstalled(&$params)
  15191. {
  15192. if (!isset($params[0])) {
  15193. return;
  15194. }
  15195. $options = $params[0]->_downloader->getOptions();
  15196. if (!isset($options['downloadonly'])) {
  15197. foreach ($params as $i => $param) {
  15198. $package = $param->getPackage();
  15199. $channel = $param->getChannel();
  15200. // remove self if already installed with this version
  15201. // this does not need any pecl magic - we only remove exact matches
  15202. if ($param->_installRegistry->packageExists($package, $channel)) {
  15203. $packageVersion = $param->_installRegistry->packageInfo($package, 'version', $channel);
  15204. if (version_compare($packageVersion, $param->getVersion(), '==')) {
  15205. if (!isset($options['force']) && !isset($options['packagingroot'])) {
  15206. $info = $param->getParsedPackage();
  15207. unset($info['version']);
  15208. unset($info['state']);
  15209. if (!isset($options['soft'])) {
  15210. $param->_downloader->log(1, 'Skipping package "' .
  15211. $param->getShortName() .
  15212. '", already installed as version ' . $packageVersion);
  15213. }
  15214. $params[$i] = false;
  15215. }
  15216. } elseif (!isset($options['force']) && !isset($options['upgrade']) &&
  15217. !isset($options['soft']) && !isset($options['packagingroot'])) {
  15218. $info = $param->getParsedPackage();
  15219. $param->_downloader->log(1, 'Skipping package "' .
  15220. $param->getShortName() .
  15221. '", already installed as version ' . $packageVersion);
  15222. $params[$i] = false;
  15223. }
  15224. }
  15225. }
  15226. }
  15227. PEAR_Downloader_Package::removeDuplicates($params);
  15228. }
  15229. function _detect2($deps, $pname, $options, $params)
  15230. {
  15231. $this->_downloadDeps = array();
  15232. $groupnotfound = false;
  15233. foreach (array('package', 'subpackage') as $packagetype) {
  15234. // get required dependency group
  15235. if (isset($deps['required'][$packagetype])) {
  15236. if (isset($deps['required'][$packagetype][0])) {
  15237. foreach ($deps['required'][$packagetype] as $dep) {
  15238. if (isset($dep['conflicts'])) {
  15239. // skip any package that this package conflicts with
  15240. continue;
  15241. }
  15242. $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
  15243. if (is_array($ret)) {
  15244. $this->_downloadDeps[] = $ret;
  15245. } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
  15246. $this->_downloader->log(0, $ret->getMessage());
  15247. }
  15248. }
  15249. } else {
  15250. $dep = $deps['required'][$packagetype];
  15251. if (!isset($dep['conflicts'])) {
  15252. // skip any package that this package conflicts with
  15253. $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
  15254. if (is_array($ret)) {
  15255. $this->_downloadDeps[] = $ret;
  15256. } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
  15257. $this->_downloader->log(0, $ret->getMessage());
  15258. }
  15259. }
  15260. }
  15261. }
  15262. // get optional dependency group, if any
  15263. if (isset($deps['optional'][$packagetype])) {
  15264. $skipnames = array();
  15265. if (!isset($deps['optional'][$packagetype][0])) {
  15266. $deps['optional'][$packagetype] = array($deps['optional'][$packagetype]);
  15267. }
  15268. foreach ($deps['optional'][$packagetype] as $dep) {
  15269. $skip = false;
  15270. if (!isset($options['alldeps'])) {
  15271. $dep['package'] = $dep['name'];
  15272. if (!isset($options['soft'])) {
  15273. $this->_downloader->log(3, 'Notice: package "' .
  15274. $this->_registry->parsedPackageNameToString($this->getParsedPackage(),
  15275. true) . '" optional dependency "' .
  15276. $this->_registry->parsedPackageNameToString(array('package' =>
  15277. $dep['name'], 'channel' => 'pear.php.net'), true) .
  15278. '" will not be automatically downloaded');
  15279. }
  15280. $skipnames[] = $this->_registry->parsedPackageNameToString($dep, true);
  15281. $skip = true;
  15282. unset($dep['package']);
  15283. }
  15284. $ret = $this->_detect2Dep($dep, $pname, 'optional', $params);
  15285. if (PEAR::isError($ret) && !isset($options['soft'])) {
  15286. $this->_downloader->log(0, $ret->getMessage());
  15287. }
  15288. if (!$ret) {
  15289. $dep['package'] = $dep['name'];
  15290. $skip = count($skipnames) ?
  15291. $skipnames[count($skipnames) - 1] : '';
  15292. if ($skip ==
  15293. $this->_registry->parsedPackageNameToString($dep, true)) {
  15294. array_pop($skipnames);
  15295. }
  15296. }
  15297. if (!$skip && is_array($ret)) {
  15298. $this->_downloadDeps[] = $ret;
  15299. }
  15300. }
  15301. if (count($skipnames)) {
  15302. if (!isset($options['soft'])) {
  15303. $this->_downloader->log(1, 'Did not download optional dependencies: ' .
  15304. implode(', ', $skipnames) .
  15305. ', use --alldeps to download automatically');
  15306. }
  15307. }
  15308. }
  15309. // get requested dependency group, if any
  15310. $groupname = $this->getGroup();
  15311. $explicit = $this->_explicitGroup;
  15312. if (!$groupname) {
  15313. if (!$this->canDefault()) {
  15314. continue;
  15315. }
  15316. $groupname = 'default'; // try the default dependency group
  15317. }
  15318. if ($groupnotfound) {
  15319. continue;
  15320. }
  15321. if (isset($deps['group'])) {
  15322. if (isset($deps['group']['attribs'])) {
  15323. if (strtolower($deps['group']['attribs']['name']) == strtolower($groupname)) {
  15324. $group = $deps['group'];
  15325. } elseif ($explicit) {
  15326. if (!isset($options['soft'])) {
  15327. $this->_downloader->log(0, 'Warning: package "' .
  15328. $this->_registry->parsedPackageNameToString($pname, true) .
  15329. '" has no dependency ' . 'group named "' . $groupname . '"');
  15330. }
  15331. $groupnotfound = true;
  15332. continue;
  15333. }
  15334. } else {
  15335. $found = false;
  15336. foreach ($deps['group'] as $group) {
  15337. if (strtolower($group['attribs']['name']) == strtolower($groupname)) {
  15338. $found = true;
  15339. break;
  15340. }
  15341. }
  15342. if (!$found) {
  15343. if ($explicit) {
  15344. if (!isset($options['soft'])) {
  15345. $this->_downloader->log(0, 'Warning: package "' .
  15346. $this->_registry->parsedPackageNameToString($pname, true) .
  15347. '" has no dependency ' . 'group named "' . $groupname . '"');
  15348. }
  15349. }
  15350. $groupnotfound = true;
  15351. continue;
  15352. }
  15353. }
  15354. }
  15355. if (isset($group) && isset($group[$packagetype])) {
  15356. if (isset($group[$packagetype][0])) {
  15357. foreach ($group[$packagetype] as $dep) {
  15358. $ret = $this->_detect2Dep($dep, $pname, 'dependency group "' .
  15359. $group['attribs']['name'] . '"', $params);
  15360. if (is_array($ret)) {
  15361. $this->_downloadDeps[] = $ret;
  15362. } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
  15363. $this->_downloader->log(0, $ret->getMessage());
  15364. }
  15365. }
  15366. } else {
  15367. $ret = $this->_detect2Dep($group[$packagetype], $pname,
  15368. 'dependency group "' .
  15369. $group['attribs']['name'] . '"', $params);
  15370. if (is_array($ret)) {
  15371. $this->_downloadDeps[] = $ret;
  15372. } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
  15373. $this->_downloader->log(0, $ret->getMessage());
  15374. }
  15375. }
  15376. }
  15377. }
  15378. }
  15379. function _detect2Dep($dep, $pname, $group, $params)
  15380. {
  15381. if (isset($dep['conflicts'])) {
  15382. return true;
  15383. }
  15384. $options = $this->_downloader->getOptions();
  15385. if (isset($dep['uri'])) {
  15386. return array('uri' => $dep['uri'], 'dep' => $dep);;
  15387. }
  15388. $testdep = $dep;
  15389. $testdep['package'] = $dep['name'];
  15390. if (PEAR_Downloader_Package::willDownload($testdep, $params)) {
  15391. $dep['package'] = $dep['name'];
  15392. if (!isset($options['soft'])) {
  15393. $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group .
  15394. ' dependency "' .
  15395. $this->_registry->parsedPackageNameToString($dep, true) .
  15396. '", will be installed');
  15397. }
  15398. return false;
  15399. }
  15400. $options = $this->_downloader->getOptions();
  15401. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  15402. if ($this->_explicitState) {
  15403. $pname['state'] = $this->_explicitState;
  15404. }
  15405. $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
  15406. if (PEAR::isError($url)) {
  15407. PEAR::popErrorHandling();
  15408. return $url;
  15409. }
  15410. $dep['package'] = $dep['name'];
  15411. $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, $group == 'optional' &&
  15412. !isset($options['alldeps']), true);
  15413. PEAR::popErrorHandling();
  15414. if (PEAR::isError($ret)) {
  15415. if (!isset($options['soft'])) {
  15416. $this->_downloader->log(0, $ret->getMessage());
  15417. }
  15418. return false;
  15419. }
  15420. // check to see if a dep is already installed and is the same or newer
  15421. if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended'])) {
  15422. $oper = 'has';
  15423. } else {
  15424. $oper = 'gt';
  15425. }
  15426. // do not try to move this before getDepPackageDownloadURL
  15427. // we can't determine whether upgrade is necessary until we know what
  15428. // version would be downloaded
  15429. if (!isset($options['force']) && $this->isInstalled($ret, $oper)) {
  15430. $version = $this->_installRegistry->packageInfo($dep['name'], 'version', $dep['channel']);
  15431. $dep['package'] = $dep['name'];
  15432. if (!isset($options['soft'])) {
  15433. $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
  15434. ' dependency "' .
  15435. $this->_registry->parsedPackageNameToString($dep, true) .
  15436. '" version ' . $url['version'] . ', already installed as version ' .
  15437. $version);
  15438. }
  15439. return false;
  15440. }
  15441. if (isset($dep['nodefault'])) {
  15442. $ret['nodefault'] = true;
  15443. }
  15444. return $ret;
  15445. }
  15446. function _detect1($deps, $pname, $options, $params)
  15447. {
  15448. $this->_downloadDeps = array();
  15449. $skipnames = array();
  15450. foreach ($deps as $dep) {
  15451. $nodownload = false;
  15452. if (isset ($dep['type']) && $dep['type'] === 'pkg') {
  15453. $dep['channel'] = 'pear.php.net';
  15454. $dep['package'] = $dep['name'];
  15455. switch ($dep['rel']) {
  15456. case 'not' :
  15457. continue 2;
  15458. case 'ge' :
  15459. case 'eq' :
  15460. case 'gt' :
  15461. case 'has' :
  15462. $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
  15463. 'required' :
  15464. 'optional';
  15465. if (PEAR_Downloader_Package::willDownload($dep, $params)) {
  15466. $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
  15467. . ' dependency "' .
  15468. $this->_registry->parsedPackageNameToString($dep, true) .
  15469. '", will be installed');
  15470. continue 2;
  15471. }
  15472. $fakedp = new PEAR_PackageFile_v1;
  15473. $fakedp->setPackage($dep['name']);
  15474. // skip internet check if we are not upgrading (bug #5810)
  15475. if (!isset($options['upgrade']) && $this->isInstalled(
  15476. $fakedp, $dep['rel'])) {
  15477. $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
  15478. . ' dependency "' .
  15479. $this->_registry->parsedPackageNameToString($dep, true) .
  15480. '", is already installed');
  15481. continue 2;
  15482. }
  15483. }
  15484. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  15485. if ($this->_explicitState) {
  15486. $pname['state'] = $this->_explicitState;
  15487. }
  15488. $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
  15489. $chan = 'pear.php.net';
  15490. if (PEAR::isError($url)) {
  15491. // check to see if this is a pecl package that has jumped
  15492. // from pear.php.net to pecl.php.net channel
  15493. if (!class_exists('PEAR_Dependency2')) {
  15494. require_once 'phar://go-pear.phar/' . 'PEAR/Dependency2.php';
  15495. }
  15496. $newdep = PEAR_Dependency2::normalizeDep($dep);
  15497. $newdep = $newdep[0];
  15498. $newdep['channel'] = 'pecl.php.net';
  15499. $chan = 'pecl.php.net';
  15500. $url = $this->_downloader->_getDepPackageDownloadUrl($newdep, $pname);
  15501. $obj = &$this->_installRegistry->getPackage($dep['name']);
  15502. if (PEAR::isError($url)) {
  15503. PEAR::popErrorHandling();
  15504. if ($obj !== null && $this->isInstalled($obj, $dep['rel'])) {
  15505. $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
  15506. 'required' :
  15507. 'optional';
  15508. $dep['package'] = $dep['name'];
  15509. if (!isset($options['soft'])) {
  15510. $this->_downloader->log(3, $this->getShortName() .
  15511. ': Skipping ' . $group . ' dependency "' .
  15512. $this->_registry->parsedPackageNameToString($dep, true) .
  15513. '", already installed as version ' . $obj->getVersion());
  15514. }
  15515. $skip = count($skipnames) ?
  15516. $skipnames[count($skipnames) - 1] : '';
  15517. if ($skip ==
  15518. $this->_registry->parsedPackageNameToString($dep, true)) {
  15519. array_pop($skipnames);
  15520. }
  15521. continue;
  15522. } else {
  15523. if (isset($dep['optional']) && $dep['optional'] == 'yes') {
  15524. $this->_downloader->log(2, $this->getShortName() .
  15525. ': Skipping optional dependency "' .
  15526. $this->_registry->parsedPackageNameToString($dep, true) .
  15527. '", no releases exist');
  15528. continue;
  15529. } else {
  15530. return $url;
  15531. }
  15532. }
  15533. }
  15534. }
  15535. PEAR::popErrorHandling();
  15536. if (!isset($options['alldeps'])) {
  15537. if (isset($dep['optional']) && $dep['optional'] == 'yes') {
  15538. if (!isset($options['soft'])) {
  15539. $this->_downloader->log(3, 'Notice: package "' .
  15540. $this->getShortName() .
  15541. '" optional dependency "' .
  15542. $this->_registry->parsedPackageNameToString(
  15543. array('channel' => $chan, 'package' =>
  15544. $dep['name']), true) .
  15545. '" will not be automatically downloaded');
  15546. }
  15547. $skipnames[] = $this->_registry->parsedPackageNameToString(
  15548. array('channel' => $chan, 'package' =>
  15549. $dep['name']), true);
  15550. $nodownload = true;
  15551. }
  15552. }
  15553. if (!isset($options['alldeps']) && !isset($options['onlyreqdeps'])) {
  15554. if (!isset($dep['optional']) || $dep['optional'] == 'no') {
  15555. if (!isset($options['soft'])) {
  15556. $this->_downloader->log(3, 'Notice: package "' .
  15557. $this->getShortName() .
  15558. '" required dependency "' .
  15559. $this->_registry->parsedPackageNameToString(
  15560. array('channel' => $chan, 'package' =>
  15561. $dep['name']), true) .
  15562. '" will not be automatically downloaded');
  15563. }
  15564. $skipnames[] = $this->_registry->parsedPackageNameToString(
  15565. array('channel' => $chan, 'package' =>
  15566. $dep['name']), true);
  15567. $nodownload = true;
  15568. }
  15569. }
  15570. // check to see if a dep is already installed
  15571. // do not try to move this before getDepPackageDownloadURL
  15572. // we can't determine whether upgrade is necessary until we know what
  15573. // version would be downloaded
  15574. if (!isset($options['force']) && $this->isInstalled(
  15575. $url, $dep['rel'])) {
  15576. $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
  15577. 'required' :
  15578. 'optional';
  15579. $dep['package'] = $dep['name'];
  15580. if (isset($newdep)) {
  15581. $version = $this->_installRegistry->packageInfo($newdep['name'], 'version', $newdep['channel']);
  15582. } else {
  15583. $version = $this->_installRegistry->packageInfo($dep['name'], 'version');
  15584. }
  15585. $dep['version'] = $url['version'];
  15586. if (!isset($options['soft'])) {
  15587. $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
  15588. ' dependency "' .
  15589. $this->_registry->parsedPackageNameToString($dep, true) .
  15590. '", already installed as version ' . $version);
  15591. }
  15592. $skip = count($skipnames) ?
  15593. $skipnames[count($skipnames) - 1] : '';
  15594. if ($skip ==
  15595. $this->_registry->parsedPackageNameToString($dep, true)) {
  15596. array_pop($skipnames);
  15597. }
  15598. continue;
  15599. }
  15600. if ($nodownload) {
  15601. continue;
  15602. }
  15603. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  15604. if (isset($newdep)) {
  15605. $dep = $newdep;
  15606. }
  15607. $dep['package'] = $dep['name'];
  15608. $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params,
  15609. isset($dep['optional']) && $dep['optional'] == 'yes' &&
  15610. !isset($options['alldeps']), true);
  15611. PEAR::popErrorHandling();
  15612. if (PEAR::isError($ret)) {
  15613. if (!isset($options['soft'])) {
  15614. $this->_downloader->log(0, $ret->getMessage());
  15615. }
  15616. continue;
  15617. }
  15618. $this->_downloadDeps[] = $ret;
  15619. }
  15620. }
  15621. if (count($skipnames)) {
  15622. if (!isset($options['soft'])) {
  15623. $this->_downloader->log(1, 'Did not download dependencies: ' .
  15624. implode(', ', $skipnames) .
  15625. ', use --alldeps or --onlyreqdeps to download automatically');
  15626. }
  15627. }
  15628. }
  15629. function setDownloadURL($pkg)
  15630. {
  15631. $this->_downloadURL = $pkg;
  15632. }
  15633. /**
  15634. * Set the package.xml object for this downloaded package
  15635. *
  15636. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 $pkg
  15637. */
  15638. function setPackageFile(&$pkg)
  15639. {
  15640. $this->_packagefile = &$pkg;
  15641. }
  15642. function getShortName()
  15643. {
  15644. return $this->_registry->parsedPackageNameToString(array('channel' => $this->getChannel(),
  15645. 'package' => $this->getPackage()), true);
  15646. }
  15647. function getParsedPackage()
  15648. {
  15649. if (isset($this->_packagefile) || isset($this->_parsedname)) {
  15650. return array('channel' => $this->getChannel(),
  15651. 'package' => $this->getPackage(),
  15652. 'version' => $this->getVersion());
  15653. }
  15654. return false;
  15655. }
  15656. function getDownloadURL()
  15657. {
  15658. return $this->_downloadURL;
  15659. }
  15660. function canDefault()
  15661. {
  15662. if (isset($this->_downloadURL) && isset($this->_downloadURL['nodefault'])) {
  15663. return false;
  15664. }
  15665. return true;
  15666. }
  15667. function getPackage()
  15668. {
  15669. if (isset($this->_packagefile)) {
  15670. return $this->_packagefile->getPackage();
  15671. } elseif (isset($this->_downloadURL['info'])) {
  15672. return $this->_downloadURL['info']->getPackage();
  15673. }
  15674. return false;
  15675. }
  15676. /**
  15677. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  15678. */
  15679. function isSubpackage(&$pf)
  15680. {
  15681. if (isset($this->_packagefile)) {
  15682. return $this->_packagefile->isSubpackage($pf);
  15683. } elseif (isset($this->_downloadURL['info'])) {
  15684. return $this->_downloadURL['info']->isSubpackage($pf);
  15685. }
  15686. return false;
  15687. }
  15688. function getPackageType()
  15689. {
  15690. if (isset($this->_packagefile)) {
  15691. return $this->_packagefile->getPackageType();
  15692. } elseif (isset($this->_downloadURL['info'])) {
  15693. return $this->_downloadURL['info']->getPackageType();
  15694. }
  15695. return false;
  15696. }
  15697. function isBundle()
  15698. {
  15699. if (isset($this->_packagefile)) {
  15700. return $this->_packagefile->getPackageType() == 'bundle';
  15701. }
  15702. return false;
  15703. }
  15704. function getPackageXmlVersion()
  15705. {
  15706. if (isset($this->_packagefile)) {
  15707. return $this->_packagefile->getPackagexmlVersion();
  15708. } elseif (isset($this->_downloadURL['info'])) {
  15709. return $this->_downloadURL['info']->getPackagexmlVersion();
  15710. }
  15711. return '1.0';
  15712. }
  15713. function getChannel()
  15714. {
  15715. if (isset($this->_packagefile)) {
  15716. return $this->_packagefile->getChannel();
  15717. } elseif (isset($this->_downloadURL['info'])) {
  15718. return $this->_downloadURL['info']->getChannel();
  15719. }
  15720. return false;
  15721. }
  15722. function getURI()
  15723. {
  15724. if (isset($this->_packagefile)) {
  15725. return $this->_packagefile->getURI();
  15726. } elseif (isset($this->_downloadURL['info'])) {
  15727. return $this->_downloadURL['info']->getURI();
  15728. }
  15729. return false;
  15730. }
  15731. function getVersion()
  15732. {
  15733. if (isset($this->_packagefile)) {
  15734. return $this->_packagefile->getVersion();
  15735. } elseif (isset($this->_downloadURL['version'])) {
  15736. return $this->_downloadURL['version'];
  15737. }
  15738. return false;
  15739. }
  15740. function isCompatible($pf)
  15741. {
  15742. if (isset($this->_packagefile)) {
  15743. return $this->_packagefile->isCompatible($pf);
  15744. } elseif (isset($this->_downloadURL['info'])) {
  15745. return $this->_downloadURL['info']->isCompatible($pf);
  15746. }
  15747. return true;
  15748. }
  15749. function setGroup($group)
  15750. {
  15751. $this->_parsedname['group'] = $group;
  15752. }
  15753. function getGroup()
  15754. {
  15755. if (isset($this->_parsedname['group'])) {
  15756. return $this->_parsedname['group'];
  15757. }
  15758. return '';
  15759. }
  15760. function isExtension($name)
  15761. {
  15762. if (isset($this->_packagefile)) {
  15763. return $this->_packagefile->isExtension($name);
  15764. } elseif (isset($this->_downloadURL['info'])) {
  15765. if ($this->_downloadURL['info']->getPackagexmlVersion() == '2.0') {
  15766. return $this->_downloadURL['info']->getProvidesExtension() == $name;
  15767. }
  15768. return false;
  15769. }
  15770. return false;
  15771. }
  15772. function getDeps()
  15773. {
  15774. if (isset($this->_packagefile)) {
  15775. $ver = $this->_packagefile->getPackagexmlVersion();
  15776. if (version_compare($ver, '2.0', '>=')) {
  15777. return $this->_packagefile->getDeps(true);
  15778. }
  15779. return $this->_packagefile->getDeps();
  15780. } elseif (isset($this->_downloadURL['info'])) {
  15781. $ver = $this->_downloadURL['info']->getPackagexmlVersion();
  15782. if (version_compare($ver, '2.0', '>=')) {
  15783. return $this->_downloadURL['info']->getDeps(true);
  15784. }
  15785. return $this->_downloadURL['info']->getDeps();
  15786. }
  15787. return array();
  15788. }
  15789. /**
  15790. * @param array Parsed array from {@link PEAR_Registry::parsePackageName()} or a dependency
  15791. * returned from getDepDownloadURL()
  15792. */
  15793. function isEqual($param)
  15794. {
  15795. if (is_object($param)) {
  15796. $channel = $param->getChannel();
  15797. $package = $param->getPackage();
  15798. if ($param->getURI()) {
  15799. $param = array(
  15800. 'channel' => $param->getChannel(),
  15801. 'package' => $param->getPackage(),
  15802. 'version' => $param->getVersion(),
  15803. 'uri' => $param->getURI(),
  15804. );
  15805. } else {
  15806. $param = array(
  15807. 'channel' => $param->getChannel(),
  15808. 'package' => $param->getPackage(),
  15809. 'version' => $param->getVersion(),
  15810. );
  15811. }
  15812. } else {
  15813. if (isset($param['uri'])) {
  15814. if ($this->getChannel() != '__uri') {
  15815. return false;
  15816. }
  15817. return $param['uri'] == $this->getURI();
  15818. }
  15819. $package = isset($param['package']) ? $param['package'] : $param['info']->getPackage();
  15820. $channel = isset($param['channel']) ? $param['channel'] : $param['info']->getChannel();
  15821. if (isset($param['rel'])) {
  15822. if (!class_exists('PEAR_Dependency2')) {
  15823. require_once 'phar://go-pear.phar/' . 'PEAR/Dependency2.php';
  15824. }
  15825. $newdep = PEAR_Dependency2::normalizeDep($param);
  15826. $newdep = $newdep[0];
  15827. } elseif (isset($param['min'])) {
  15828. $newdep = $param;
  15829. }
  15830. }
  15831. if (isset($newdep)) {
  15832. if (!isset($newdep['min'])) {
  15833. $newdep['min'] = '0';
  15834. }
  15835. if (!isset($newdep['max'])) {
  15836. $newdep['max'] = '100000000000000000000';
  15837. }
  15838. // use magic to support pecl packages suddenly jumping to the pecl channel
  15839. // we need to support both dependency possibilities
  15840. if ($channel == 'pear.php.net' && $this->getChannel() == 'pecl.php.net') {
  15841. if ($package == $this->getPackage()) {
  15842. $channel = 'pecl.php.net';
  15843. }
  15844. }
  15845. if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
  15846. if ($package == $this->getPackage()) {
  15847. $channel = 'pear.php.net';
  15848. }
  15849. }
  15850. return (strtolower($package) == strtolower($this->getPackage()) &&
  15851. $channel == $this->getChannel() &&
  15852. version_compare($newdep['min'], $this->getVersion(), '<=') &&
  15853. version_compare($newdep['max'], $this->getVersion(), '>='));
  15854. }
  15855. // use magic to support pecl packages suddenly jumping to the pecl channel
  15856. if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
  15857. if (strtolower($package) == strtolower($this->getPackage())) {
  15858. $channel = 'pear.php.net';
  15859. }
  15860. }
  15861. if (isset($param['version'])) {
  15862. return (strtolower($package) == strtolower($this->getPackage()) &&
  15863. $channel == $this->getChannel() &&
  15864. $param['version'] == $this->getVersion());
  15865. }
  15866. return strtolower($package) == strtolower($this->getPackage()) &&
  15867. $channel == $this->getChannel();
  15868. }
  15869. function isInstalled($dep, $oper = '==')
  15870. {
  15871. if (!$dep) {
  15872. return false;
  15873. }
  15874. if ($oper != 'ge' && $oper != 'gt' && $oper != 'has' && $oper != '==') {
  15875. return false;
  15876. }
  15877. if (is_object($dep)) {
  15878. $package = $dep->getPackage();
  15879. $channel = $dep->getChannel();
  15880. if ($dep->getURI()) {
  15881. $dep = array(
  15882. 'uri' => $dep->getURI(),
  15883. 'version' => $dep->getVersion(),
  15884. );
  15885. } else {
  15886. $dep = array(
  15887. 'version' => $dep->getVersion(),
  15888. );
  15889. }
  15890. } else {
  15891. if (isset($dep['uri'])) {
  15892. $channel = '__uri';
  15893. $package = $dep['dep']['name'];
  15894. } else {
  15895. $channel = $dep['info']->getChannel();
  15896. $package = $dep['info']->getPackage();
  15897. }
  15898. }
  15899. $options = $this->_downloader->getOptions();
  15900. $test = $this->_installRegistry->packageExists($package, $channel);
  15901. if (!$test && $channel == 'pecl.php.net') {
  15902. // do magic to allow upgrading from old pecl packages to new ones
  15903. $test = $this->_installRegistry->packageExists($package, 'pear.php.net');
  15904. $channel = 'pear.php.net';
  15905. }
  15906. if ($test) {
  15907. if (isset($dep['uri'])) {
  15908. if ($this->_installRegistry->packageInfo($package, 'uri', '__uri') == $dep['uri']) {
  15909. return true;
  15910. }
  15911. }
  15912. if (isset($options['upgrade'])) {
  15913. $packageVersion = $this->_installRegistry->packageInfo($package, 'version', $channel);
  15914. if (version_compare($packageVersion, $dep['version'], '>=')) {
  15915. return true;
  15916. }
  15917. return false;
  15918. }
  15919. return true;
  15920. }
  15921. return false;
  15922. }
  15923. /**
  15924. * Detect duplicate package names with differing versions
  15925. *
  15926. * If a user requests to install Date 1.4.6 and Date 1.4.7,
  15927. * for instance, this is a logic error. This method
  15928. * detects this situation.
  15929. *
  15930. * @param array $params array of PEAR_Downloader_Package objects
  15931. * @param array $errorparams empty array
  15932. * @return array array of stupid duplicated packages in PEAR_Downloader_Package obejcts
  15933. */
  15934. public static function detectStupidDuplicates($params, &$errorparams)
  15935. {
  15936. $existing = array();
  15937. foreach ($params as $i => $param) {
  15938. $package = $param->getPackage();
  15939. $channel = $param->getChannel();
  15940. $group = $param->getGroup();
  15941. if (!isset($existing[$channel . '/' . $package])) {
  15942. $existing[$channel . '/' . $package] = array();
  15943. }
  15944. if (!isset($existing[$channel . '/' . $package][$group])) {
  15945. $existing[$channel . '/' . $package][$group] = array();
  15946. }
  15947. $existing[$channel . '/' . $package][$group][] = $i;
  15948. }
  15949. $indices = array();
  15950. foreach ($existing as $package => $groups) {
  15951. foreach ($groups as $group => $dupes) {
  15952. if (count($dupes) > 1) {
  15953. $indices = $indices + $dupes;
  15954. }
  15955. }
  15956. }
  15957. $indices = array_unique($indices);
  15958. foreach ($indices as $index) {
  15959. $errorparams[] = $params[$index];
  15960. }
  15961. return count($errorparams);
  15962. }
  15963. /**
  15964. * @param array
  15965. * @param bool ignore install groups - for final removal of dupe packages
  15966. */
  15967. public static function removeDuplicates(&$params, $ignoreGroups = false)
  15968. {
  15969. $pnames = array();
  15970. foreach ($params as $i => $param) {
  15971. if (!$param) {
  15972. continue;
  15973. }
  15974. if ($param->getPackage()) {
  15975. $group = $ignoreGroups ? '' : $param->getGroup();
  15976. $pnames[$i] = $param->getChannel() . '/' .
  15977. $param->getPackage() . '-' . $param->getVersion() . '#' . $group;
  15978. }
  15979. }
  15980. $pnames = array_unique($pnames);
  15981. $unset = array_diff(array_keys($params), array_keys($pnames));
  15982. $testp = array_flip($pnames);
  15983. foreach ($params as $i => $param) {
  15984. if (!$param) {
  15985. $unset[] = $i;
  15986. continue;
  15987. }
  15988. if (!is_a($param, 'PEAR_Downloader_Package')) {
  15989. $unset[] = $i;
  15990. continue;
  15991. }
  15992. $group = $ignoreGroups ? '' : $param->getGroup();
  15993. if (!isset($testp[$param->getChannel() . '/' . $param->getPackage() . '-' .
  15994. $param->getVersion() . '#' . $group])) {
  15995. $unset[] = $i;
  15996. }
  15997. }
  15998. foreach ($unset as $i) {
  15999. unset($params[$i]);
  16000. }
  16001. $ret = array();
  16002. foreach ($params as $i => $param) {
  16003. $ret[] = &$params[$i];
  16004. }
  16005. $params = array();
  16006. foreach ($ret as $i => $param) {
  16007. $params[] = &$ret[$i];
  16008. }
  16009. }
  16010. function explicitState()
  16011. {
  16012. return $this->_explicitState;
  16013. }
  16014. function setExplicitState($s)
  16015. {
  16016. $this->_explicitState = $s;
  16017. }
  16018. /**
  16019. */
  16020. public static function mergeDependencies(&$params)
  16021. {
  16022. $bundles = $newparams = array();
  16023. foreach ($params as $i => $param) {
  16024. if (!$param->isBundle()) {
  16025. continue;
  16026. }
  16027. $bundles[] = $i;
  16028. $pf = &$param->getPackageFile();
  16029. $newdeps = array();
  16030. $contents = $pf->getBundledPackages();
  16031. if (!is_array($contents)) {
  16032. $contents = array($contents);
  16033. }
  16034. foreach ($contents as $file) {
  16035. $filecontents = $pf->getFileContents($file);
  16036. $dl = &$param->getDownloader();
  16037. $options = $dl->getOptions();
  16038. if (PEAR::isError($dir = $dl->getDownloadDir())) {
  16039. return $dir;
  16040. }
  16041. $fp = @fopen($dir . DIRECTORY_SEPARATOR . $file, 'wb');
  16042. if (!$fp) {
  16043. continue;
  16044. }
  16045. // FIXME do symlink check
  16046. fwrite($fp, $filecontents, strlen($filecontents));
  16047. fclose($fp);
  16048. if ($s = $params[$i]->explicitState()) {
  16049. $obj->setExplicitState($s);
  16050. }
  16051. $obj = new PEAR_Downloader_Package($params[$i]->getDownloader());
  16052. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  16053. if (PEAR::isError($dir = $dl->getDownloadDir())) {
  16054. PEAR::popErrorHandling();
  16055. return $dir;
  16056. }
  16057. $a = $dir . DIRECTORY_SEPARATOR . $file;
  16058. $e = $obj->_fromFile($a);
  16059. PEAR::popErrorHandling();
  16060. if (PEAR::isError($e)) {
  16061. if (!isset($options['soft'])) {
  16062. $dl->log(0, $e->getMessage());
  16063. }
  16064. continue;
  16065. }
  16066. if (!PEAR_Downloader_Package::willDownload($obj,
  16067. array_merge($params, $newparams)) && !$param->isInstalled($obj)) {
  16068. $newparams[] = $obj;
  16069. }
  16070. }
  16071. }
  16072. foreach ($bundles as $i) {
  16073. unset($params[$i]); // remove bundles - only their contents matter for installation
  16074. }
  16075. PEAR_Downloader_Package::removeDuplicates($params); // strip any unset indices
  16076. if (count($newparams)) { // add in bundled packages for install
  16077. foreach ($newparams as $i => $unused) {
  16078. $params[] = &$newparams[$i];
  16079. }
  16080. $newparams = array();
  16081. }
  16082. foreach ($params as $i => $param) {
  16083. $newdeps = array();
  16084. foreach ($param->_downloadDeps as $dep) {
  16085. $merge = array_merge($params, $newparams);
  16086. if (!PEAR_Downloader_Package::willDownload($dep, $merge)
  16087. && !$param->isInstalled($dep)
  16088. ) {
  16089. $newdeps[] = $dep;
  16090. } else {
  16091. //var_dump($dep);
  16092. // detect versioning conflicts here
  16093. }
  16094. }
  16095. // convert the dependencies into PEAR_Downloader_Package objects for the next time around
  16096. $params[$i]->_downloadDeps = array();
  16097. foreach ($newdeps as $dep) {
  16098. $obj = new PEAR_Downloader_Package($params[$i]->getDownloader());
  16099. if ($s = $params[$i]->explicitState()) {
  16100. $obj->setExplicitState($s);
  16101. }
  16102. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  16103. $e = $obj->fromDepURL($dep);
  16104. PEAR::popErrorHandling();
  16105. if (PEAR::isError($e)) {
  16106. if (!isset($options['soft'])) {
  16107. $obj->_downloader->log(0, $e->getMessage());
  16108. }
  16109. continue;
  16110. }
  16111. $e = $obj->detectDependencies($params);
  16112. if (PEAR::isError($e)) {
  16113. if (!isset($options['soft'])) {
  16114. $obj->_downloader->log(0, $e->getMessage());
  16115. }
  16116. }
  16117. $newparams[] = $obj;
  16118. }
  16119. }
  16120. if (count($newparams)) {
  16121. foreach ($newparams as $i => $unused) {
  16122. $params[] = &$newparams[$i];
  16123. }
  16124. return true;
  16125. }
  16126. return false;
  16127. }
  16128. /**
  16129. */
  16130. public static function willDownload($param, $params)
  16131. {
  16132. if (!is_array($params)) {
  16133. return false;
  16134. }
  16135. foreach ($params as $obj) {
  16136. if ($obj->isEqual($param)) {
  16137. return true;
  16138. }
  16139. }
  16140. return false;
  16141. }
  16142. /**
  16143. * For simpler unit-testing
  16144. * @param PEAR_Config
  16145. * @param int
  16146. * @param string
  16147. */
  16148. function &getPackagefileObject(&$c, $d)
  16149. {
  16150. $a = new PEAR_PackageFile($c, $d);
  16151. return $a;
  16152. }
  16153. /**
  16154. * This will retrieve from a local file if possible, and parse out
  16155. * a group name as well. The original parameter will be modified to reflect this.
  16156. * @param string|array can be a parsed package name as well
  16157. * @access private
  16158. */
  16159. function _fromFile(&$param)
  16160. {
  16161. $saveparam = $param;
  16162. if (is_string($param) && substr($param, 0, 10) !== 'channel://') {
  16163. if (!@file_exists($param)) {
  16164. $test = explode('#', $param);
  16165. $group = array_pop($test);
  16166. if (@file_exists(implode('#', $test))) {
  16167. $this->setGroup($group);
  16168. $param = implode('#', $test);
  16169. $this->_explicitGroup = true;
  16170. }
  16171. }
  16172. if (@is_file($param)) {
  16173. $this->_type = 'local';
  16174. $options = $this->_downloader->getOptions();
  16175. $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->_debug);
  16176. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  16177. $pf = &$pkg->fromAnyFile($param, PEAR_VALIDATE_INSTALLING);
  16178. PEAR::popErrorHandling();
  16179. if (PEAR::isError($pf)) {
  16180. $this->_valid = false;
  16181. $param = $saveparam;
  16182. return $pf;
  16183. }
  16184. $this->_packagefile = &$pf;
  16185. if (!$this->getGroup()) {
  16186. $this->setGroup('default'); // install the default dependency group
  16187. }
  16188. return $this->_valid = true;
  16189. }
  16190. }
  16191. $param = $saveparam;
  16192. return $this->_valid = false;
  16193. }
  16194. function _fromUrl($param, $saveparam = '')
  16195. {
  16196. if (!is_array($param) && (preg_match('#^(http|https|ftp)://#', $param))) {
  16197. $options = $this->_downloader->getOptions();
  16198. $this->_type = 'url';
  16199. $callback = $this->_downloader->ui ?
  16200. array(&$this->_downloader, '_downloadCallback') : null;
  16201. $this->_downloader->pushErrorHandling(PEAR_ERROR_RETURN);
  16202. if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) {
  16203. $this->_downloader->popErrorHandling();
  16204. return $dir;
  16205. }
  16206. $this->_downloader->log(3, 'Downloading "' . $param . '"');
  16207. $file = $this->_downloader->downloadHttp($param, $this->_downloader->ui,
  16208. $dir, $callback, null, false, $this->getChannel());
  16209. $this->_downloader->popErrorHandling();
  16210. if (PEAR::isError($file)) {
  16211. if (!empty($saveparam)) {
  16212. $saveparam = ", cannot download \"$saveparam\"";
  16213. }
  16214. $err = PEAR::raiseError('Could not download from "' . $param .
  16215. '"' . $saveparam . ' (' . $file->getMessage() . ')');
  16216. return $err;
  16217. }
  16218. if ($this->_rawpackagefile) {
  16219. require_once 'phar://go-pear.phar/' . 'Archive/Tar.php';
  16220. $tar = new Archive_Tar($file);
  16221. $packagexml = $tar->extractInString('package2.xml');
  16222. if (!$packagexml) {
  16223. $packagexml = $tar->extractInString('package.xml');
  16224. }
  16225. if (str_replace(array("\n", "\r"), array('',''), $packagexml) !=
  16226. str_replace(array("\n", "\r"), array('',''), $this->_rawpackagefile)) {
  16227. if ($this->getChannel() != 'pear.php.net') {
  16228. return PEAR::raiseError('CRITICAL ERROR: package.xml downloaded does ' .
  16229. 'not match value returned from xml-rpc');
  16230. }
  16231. // be more lax for the existing PEAR packages that have not-ok
  16232. // characters in their package.xml
  16233. $this->_downloader->log(0, 'CRITICAL WARNING: The "' .
  16234. $this->getPackage() . '" package has invalid characters in its ' .
  16235. 'package.xml. The next version of PEAR may not be able to install ' .
  16236. 'this package for security reasons. Please open a bug report at ' .
  16237. 'http://pear.php.net/package/' . $this->getPackage() . '/bugs');
  16238. }
  16239. }
  16240. // whew, download worked!
  16241. $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug);
  16242. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  16243. $pf = &$pkg->fromAnyFile($file, PEAR_VALIDATE_INSTALLING);
  16244. PEAR::popErrorHandling();
  16245. if (PEAR::isError($pf)) {
  16246. if (is_array($pf->getUserInfo())) {
  16247. foreach ($pf->getUserInfo() as $err) {
  16248. if (is_array($err)) {
  16249. $err = $err['message'];
  16250. }
  16251. if (!isset($options['soft'])) {
  16252. $this->_downloader->log(0, "Validation Error: $err");
  16253. }
  16254. }
  16255. }
  16256. if (!isset($options['soft'])) {
  16257. $this->_downloader->log(0, $pf->getMessage());
  16258. }
  16259. ///FIXME need to pass back some error code that we can use to match with to cancel all further operations
  16260. /// At least stop all deps of this package from being installed
  16261. $out = $saveparam ? $saveparam : $param;
  16262. $err = PEAR::raiseError('Download of "' . $out . '" succeeded, but it is not a valid package archive');
  16263. $this->_valid = false;
  16264. return $err;
  16265. }
  16266. $this->_packagefile = &$pf;
  16267. $this->setGroup('default'); // install the default dependency group
  16268. return $this->_valid = true;
  16269. }
  16270. return $this->_valid = false;
  16271. }
  16272. /**
  16273. *
  16274. * @param string|array pass in an array of format
  16275. * array(
  16276. * 'package' => 'pname',
  16277. * ['channel' => 'channame',]
  16278. * ['version' => 'version',]
  16279. * ['state' => 'state',])
  16280. * or a string of format [channame/]pname[-version|-state]
  16281. */
  16282. function _fromString($param)
  16283. {
  16284. $options = $this->_downloader->getOptions();
  16285. $channel = $this->_config->get('default_channel');
  16286. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  16287. $pname = $this->_registry->parsePackageName($param, $channel);
  16288. PEAR::popErrorHandling();
  16289. if (PEAR::isError($pname)) {
  16290. if ($pname->getCode() == 'invalid') {
  16291. $this->_valid = false;
  16292. return false;
  16293. }
  16294. if ($pname->getCode() == 'channel') {
  16295. $parsed = $pname->getUserInfo();
  16296. if ($this->_downloader->discover($parsed['channel'])) {
  16297. if ($this->_config->get('auto_discover')) {
  16298. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  16299. $pname = $this->_registry->parsePackageName($param, $channel);
  16300. PEAR::popErrorHandling();
  16301. } else {
  16302. if (!isset($options['soft'])) {
  16303. $this->_downloader->log(0, 'Channel "' . $parsed['channel'] .
  16304. '" is not initialized, use ' .
  16305. '"pear channel-discover ' . $parsed['channel'] . '" to initialize' .
  16306. 'or pear config-set auto_discover 1');
  16307. }
  16308. }
  16309. }
  16310. if (PEAR::isError($pname)) {
  16311. if (!isset($options['soft'])) {
  16312. $this->_downloader->log(0, $pname->getMessage());
  16313. }
  16314. if (is_array($param)) {
  16315. $param = $this->_registry->parsedPackageNameToString($param);
  16316. }
  16317. $err = PEAR::raiseError('invalid package name/package file "' . $param . '"');
  16318. $this->_valid = false;
  16319. return $err;
  16320. }
  16321. } else {
  16322. if (!isset($options['soft'])) {
  16323. $this->_downloader->log(0, $pname->getMessage());
  16324. }
  16325. $err = PEAR::raiseError('invalid package name/package file "' . $param . '"');
  16326. $this->_valid = false;
  16327. return $err;
  16328. }
  16329. }
  16330. if (!isset($this->_type)) {
  16331. $this->_type = 'rest';
  16332. }
  16333. $this->_parsedname = $pname;
  16334. $this->_explicitState = isset($pname['state']) ? $pname['state'] : false;
  16335. $this->_explicitGroup = isset($pname['group']) ? true : false;
  16336. $info = $this->_downloader->_getPackageDownloadUrl($pname);
  16337. if (PEAR::isError($info)) {
  16338. if ($info->getCode() != -976 && $pname['channel'] == 'pear.php.net') {
  16339. // try pecl
  16340. $pname['channel'] = 'pecl.php.net';
  16341. if ($test = $this->_downloader->_getPackageDownloadUrl($pname)) {
  16342. if (!PEAR::isError($test)) {
  16343. $info = PEAR::raiseError($info->getMessage() . ' - package ' .
  16344. $this->_registry->parsedPackageNameToString($pname, true) .
  16345. ' can be installed with "pecl install ' . $pname['package'] .
  16346. '"');
  16347. } else {
  16348. $pname['channel'] = 'pear.php.net';
  16349. }
  16350. } else {
  16351. $pname['channel'] = 'pear.php.net';
  16352. }
  16353. }
  16354. return $info;
  16355. }
  16356. $this->_rawpackagefile = $info['raw'];
  16357. $ret = $this->_analyzeDownloadURL($info, $param, $pname);
  16358. if (PEAR::isError($ret)) {
  16359. return $ret;
  16360. }
  16361. if ($ret) {
  16362. $this->_downloadURL = $ret;
  16363. return $this->_valid = (bool) $ret;
  16364. }
  16365. }
  16366. /**
  16367. * @param array output of package.getDownloadURL
  16368. * @param string|array|object information for detecting packages to be downloaded, and
  16369. * for errors
  16370. * @param array name information of the package
  16371. * @param array|null packages to be downloaded
  16372. * @param bool is this an optional dependency?
  16373. * @param bool is this any kind of dependency?
  16374. * @access private
  16375. */
  16376. function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = false,
  16377. $isdependency = false)
  16378. {
  16379. if (!is_string($param) && PEAR_Downloader_Package::willDownload($param, $params)) {
  16380. return false;
  16381. }
  16382. if ($info === false) {
  16383. $saveparam = !is_string($param) ? ", cannot download \"$param\"" : '';
  16384. // no releases exist
  16385. return PEAR::raiseError('No releases for package "' .
  16386. $this->_registry->parsedPackageNameToString($pname, true) . '" exist' . $saveparam);
  16387. }
  16388. if (strtolower($info['info']->getChannel()) != strtolower($pname['channel'])) {
  16389. $err = false;
  16390. if ($pname['channel'] == 'pecl.php.net') {
  16391. if ($info['info']->getChannel() != 'pear.php.net') {
  16392. $err = true;
  16393. }
  16394. } elseif ($info['info']->getChannel() == 'pecl.php.net') {
  16395. if ($pname['channel'] != 'pear.php.net') {
  16396. $err = true;
  16397. }
  16398. } else {
  16399. $err = true;
  16400. }
  16401. if ($err) {
  16402. return PEAR::raiseError('SECURITY ERROR: package in channel "' . $pname['channel'] .
  16403. '" retrieved another channel\'s name for download! ("' .
  16404. $info['info']->getChannel() . '")');
  16405. }
  16406. }
  16407. $preferred_state = $this->_config->get('preferred_state');
  16408. if (!isset($info['url'])) {
  16409. $package_version = $this->_registry->packageInfo($info['info']->getPackage(),
  16410. 'version', $info['info']->getChannel());
  16411. if ($this->isInstalled($info)) {
  16412. if ($isdependency && version_compare($info['version'], $package_version, '<=')) {
  16413. // ignore bogus errors of "failed to download dependency"
  16414. // if it is already installed and the one that would be
  16415. // downloaded is older or the same version (Bug #7219)
  16416. return false;
  16417. }
  16418. }
  16419. if ($info['version'] === $package_version) {
  16420. if (!isset($options['soft'])) {
  16421. $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
  16422. '/' . $pname['package'] . '-' . $package_version. ', additionally the suggested version' .
  16423. ' (' . $package_version . ') is the same as the locally installed one.');
  16424. }
  16425. return false;
  16426. }
  16427. if (version_compare($info['version'], $package_version, '<=')) {
  16428. if (!isset($options['soft'])) {
  16429. $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
  16430. '/' . $pname['package'] . '-' . $package_version . ', additionally the suggested version' .
  16431. ' (' . $info['version'] . ') is a lower version than the locally installed one (' . $package_version . ').');
  16432. }
  16433. return false;
  16434. }
  16435. $instead = ', will instead download version ' . $info['version'] .
  16436. ', stability "' . $info['info']->getState() . '"';
  16437. // releases exist, but we failed to get any
  16438. if (isset($this->_downloader->_options['force'])) {
  16439. if (isset($pname['version'])) {
  16440. $vs = ', version "' . $pname['version'] . '"';
  16441. } elseif (isset($pname['state'])) {
  16442. $vs = ', stability "' . $pname['state'] . '"';
  16443. } elseif ($param == 'dependency') {
  16444. if (!class_exists('PEAR_Common')) {
  16445. require_once 'phar://go-pear.phar/' . 'PEAR/Common.php';
  16446. }
  16447. if (!in_array($info['info']->getState(),
  16448. PEAR_Common::betterStates($preferred_state, true))) {
  16449. if ($optional) {
  16450. // don't spit out confusing error message
  16451. return $this->_downloader->_getPackageDownloadUrl(
  16452. array('package' => $pname['package'],
  16453. 'channel' => $pname['channel'],
  16454. 'version' => $info['version']));
  16455. }
  16456. $vs = ' within preferred state "' . $preferred_state .
  16457. '"';
  16458. } else {
  16459. if (!class_exists('PEAR_Dependency2')) {
  16460. require_once 'phar://go-pear.phar/' . 'PEAR/Dependency2.php';
  16461. }
  16462. if ($optional) {
  16463. // don't spit out confusing error message
  16464. return $this->_downloader->_getPackageDownloadUrl(
  16465. array('package' => $pname['package'],
  16466. 'channel' => $pname['channel'],
  16467. 'version' => $info['version']));
  16468. }
  16469. $vs = PEAR_Dependency2::_getExtraString($pname);
  16470. $instead = '';
  16471. }
  16472. } else {
  16473. $vs = ' within preferred state "' . $preferred_state . '"';
  16474. }
  16475. if (!isset($options['soft'])) {
  16476. $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
  16477. '/' . $pname['package'] . $vs . $instead);
  16478. }
  16479. // download the latest release
  16480. return $this->_downloader->_getPackageDownloadUrl(
  16481. array('package' => $pname['package'],
  16482. 'channel' => $pname['channel'],
  16483. 'version' => $info['version']));
  16484. } else {
  16485. if (isset($info['php']) && $info['php']) {
  16486. $err = PEAR::raiseError('Failed to download ' .
  16487. $this->_registry->parsedPackageNameToString(
  16488. array('channel' => $pname['channel'],
  16489. 'package' => $pname['package']),
  16490. true) .
  16491. ', latest release is version ' . $info['php']['v'] .
  16492. ', but it requires PHP version "' .
  16493. $info['php']['m'] . '", use "' .
  16494. $this->_registry->parsedPackageNameToString(
  16495. array('channel' => $pname['channel'], 'package' => $pname['package'],
  16496. 'version' => $info['php']['v'])) . '" to install',
  16497. PEAR_DOWNLOADER_PACKAGE_PHPVERSION);
  16498. return $err;
  16499. }
  16500. // construct helpful error message
  16501. if (isset($pname['version'])) {
  16502. $vs = ', version "' . $pname['version'] . '"';
  16503. } elseif (isset($pname['state'])) {
  16504. $vs = ', stability "' . $pname['state'] . '"';
  16505. } elseif ($param == 'dependency') {
  16506. if (!class_exists('PEAR_Common')) {
  16507. require_once 'phar://go-pear.phar/' . 'PEAR/Common.php';
  16508. }
  16509. if (!in_array($info['info']->getState(),
  16510. PEAR_Common::betterStates($preferred_state, true))) {
  16511. if ($optional) {
  16512. // don't spit out confusing error message, and don't die on
  16513. // optional dep failure!
  16514. return $this->_downloader->_getPackageDownloadUrl(
  16515. array('package' => $pname['package'],
  16516. 'channel' => $pname['channel'],
  16517. 'version' => $info['version']));
  16518. }
  16519. $vs = ' within preferred state "' . $preferred_state . '"';
  16520. } else {
  16521. if (!class_exists('PEAR_Dependency2')) {
  16522. require_once 'phar://go-pear.phar/' . 'PEAR/Dependency2.php';
  16523. }
  16524. if ($optional) {
  16525. // don't spit out confusing error message, and don't die on
  16526. // optional dep failure!
  16527. return $this->_downloader->_getPackageDownloadUrl(
  16528. array('package' => $pname['package'],
  16529. 'channel' => $pname['channel'],
  16530. 'version' => $info['version']));
  16531. }
  16532. $vs = PEAR_Dependency2::_getExtraString($pname);
  16533. }
  16534. } else {
  16535. $vs = ' within preferred state "' . $this->_downloader->config->get('preferred_state') . '"';
  16536. }
  16537. $options = $this->_downloader->getOptions();
  16538. // this is only set by the "download-all" command
  16539. if (isset($options['ignorepreferred_state'])) {
  16540. $err = PEAR::raiseError(
  16541. 'Failed to download ' . $this->_registry->parsedPackageNameToString(
  16542. array('channel' => $pname['channel'], 'package' => $pname['package']),
  16543. true)
  16544. . $vs .
  16545. ', latest release is version ' . $info['version'] .
  16546. ', stability "' . $info['info']->getState() . '", use "' .
  16547. $this->_registry->parsedPackageNameToString(
  16548. array('channel' => $pname['channel'], 'package' => $pname['package'],
  16549. 'version' => $info['version'])) . '" to install',
  16550. PEAR_DOWNLOADER_PACKAGE_STATE);
  16551. return $err;
  16552. }
  16553. // Checks if the user has a package installed already and checks the release against
  16554. // the state against the installed package, this allows upgrades for packages
  16555. // with lower stability than the preferred_state
  16556. $stability = $this->_registry->packageInfo($pname['package'], 'stability', $pname['channel']);
  16557. if (!$this->isInstalled($info)
  16558. || !in_array($info['info']->getState(), PEAR_Common::betterStates($stability['release'], true))
  16559. ) {
  16560. $err = PEAR::raiseError(
  16561. 'Failed to download ' . $this->_registry->parsedPackageNameToString(
  16562. array('channel' => $pname['channel'], 'package' => $pname['package']),
  16563. true)
  16564. . $vs .
  16565. ', latest release is version ' . $info['version'] .
  16566. ', stability "' . $info['info']->getState() . '", use "' .
  16567. $this->_registry->parsedPackageNameToString(
  16568. array('channel' => $pname['channel'], 'package' => $pname['package'],
  16569. 'version' => $info['version'])) . '" to install');
  16570. return $err;
  16571. }
  16572. }
  16573. }
  16574. if (isset($info['deprecated']) && $info['deprecated']) {
  16575. $this->_downloader->log(0,
  16576. 'WARNING: "' .
  16577. $this->_registry->parsedPackageNameToString(
  16578. array('channel' => $info['info']->getChannel(),
  16579. 'package' => $info['info']->getPackage()), true) .
  16580. '" is deprecated in favor of "' .
  16581. $this->_registry->parsedPackageNameToString($info['deprecated'], true) .
  16582. '"');
  16583. }
  16584. return $info;
  16585. }
  16586. }
  16587. <?php
  16588. /**
  16589. * Error Stack Implementation
  16590. *
  16591. * This is an incredibly simple implementation of a very complex error handling
  16592. * facility. It contains the ability
  16593. * to track multiple errors from multiple packages simultaneously. In addition,
  16594. * it can track errors of many levels, save data along with the error, context
  16595. * information such as the exact file, line number, class and function that
  16596. * generated the error, and if necessary, it can raise a traditional PEAR_Error.
  16597. * It has built-in support for PEAR::Log, to log errors as they occur
  16598. *
  16599. * Since version 0.2alpha, it is also possible to selectively ignore errors,
  16600. * through the use of an error callback, see {@link pushCallback()}
  16601. *
  16602. * Since version 0.3alpha, it is possible to specify the exception class
  16603. * returned from {@link push()}
  16604. *
  16605. * Since version PEAR1.3.2, ErrorStack no longer instantiates an exception class. This can
  16606. * still be done quite handily in an error callback or by manipulating the returned array
  16607. * @category Debugging
  16608. * @package PEAR_ErrorStack
  16609. * @author Greg Beaver <cellog@php.net>
  16610. * @copyright 2004-2008 Greg Beaver
  16611. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  16612. * @link http://pear.php.net/package/PEAR_ErrorStack
  16613. */
  16614. /**
  16615. * Singleton storage
  16616. *
  16617. * Format:
  16618. * <pre>
  16619. * array(
  16620. * 'package1' => PEAR_ErrorStack object,
  16621. * 'package2' => PEAR_ErrorStack object,
  16622. * ...
  16623. * )
  16624. * </pre>
  16625. * @access private
  16626. * @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON']
  16627. */
  16628. $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array();
  16629. /**
  16630. * Global error callback (default)
  16631. *
  16632. * This is only used if set to non-false. * is the default callback for
  16633. * all packages, whereas specific packages may set a default callback
  16634. * for all instances, regardless of whether they are a singleton or not.
  16635. *
  16636. * To exclude non-singletons, only set the local callback for the singleton
  16637. * @see PEAR_ErrorStack::setDefaultCallback()
  16638. * @access private
  16639. * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']
  16640. */
  16641. $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array(
  16642. '*' => false,
  16643. );
  16644. /**
  16645. * Global Log object (default)
  16646. *
  16647. * This is only used if set to non-false. Use to set a default log object for
  16648. * all stacks, regardless of instantiation order or location
  16649. * @see PEAR_ErrorStack::setDefaultLogger()
  16650. * @access private
  16651. * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
  16652. */
  16653. $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false;
  16654. /**
  16655. * Global Overriding Callback
  16656. *
  16657. * This callback will override any error callbacks that specific loggers have set.
  16658. * Use with EXTREME caution
  16659. * @see PEAR_ErrorStack::staticPushCallback()
  16660. * @access private
  16661. * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
  16662. */
  16663. $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
  16664. /**#@+
  16665. * One of four possible return values from the error Callback
  16666. * @see PEAR_ErrorStack::_errorCallback()
  16667. */
  16668. /**
  16669. * If this is returned, then the error will be both pushed onto the stack
  16670. * and logged.
  16671. */
  16672. define('PEAR_ERRORSTACK_PUSHANDLOG', 1);
  16673. /**
  16674. * If this is returned, then the error will only be pushed onto the stack,
  16675. * and not logged.
  16676. */
  16677. define('PEAR_ERRORSTACK_PUSH', 2);
  16678. /**
  16679. * If this is returned, then the error will only be logged, but not pushed
  16680. * onto the error stack.
  16681. */
  16682. define('PEAR_ERRORSTACK_LOG', 3);
  16683. /**
  16684. * If this is returned, then the error is completely ignored.
  16685. */
  16686. define('PEAR_ERRORSTACK_IGNORE', 4);
  16687. /**
  16688. * If this is returned, then the error is logged and die() is called.
  16689. */
  16690. define('PEAR_ERRORSTACK_DIE', 5);
  16691. /**#@-*/
  16692. /**
  16693. * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in
  16694. * the singleton method.
  16695. */
  16696. define('PEAR_ERRORSTACK_ERR_NONCLASS', 1);
  16697. /**
  16698. * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()}
  16699. * that has no __toString() method
  16700. */
  16701. define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2);
  16702. /**
  16703. * Error Stack Implementation
  16704. *
  16705. * Usage:
  16706. * <code>
  16707. * // global error stack
  16708. * $global_stack = &PEAR_ErrorStack::singleton('MyPackage');
  16709. * // local error stack
  16710. * $local_stack = new PEAR_ErrorStack('MyPackage');
  16711. * </code>
  16712. * @author Greg Beaver <cellog@php.net>
  16713. * @version 1.10.16
  16714. * @package PEAR_ErrorStack
  16715. * @category Debugging
  16716. * @copyright 2004-2008 Greg Beaver
  16717. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  16718. * @link http://pear.php.net/package/PEAR_ErrorStack
  16719. */
  16720. class PEAR_ErrorStack {
  16721. /**
  16722. * Errors are stored in the order that they are pushed on the stack.
  16723. * @since 0.4alpha Errors are no longer organized by error level.
  16724. * This renders pop() nearly unusable, and levels could be more easily
  16725. * handled in a callback anyway
  16726. * @var array
  16727. * @access private
  16728. */
  16729. var $_errors = array();
  16730. /**
  16731. * Storage of errors by level.
  16732. *
  16733. * Allows easy retrieval and deletion of only errors from a particular level
  16734. * @since PEAR 1.4.0dev
  16735. * @var array
  16736. * @access private
  16737. */
  16738. var $_errorsByLevel = array();
  16739. /**
  16740. * Package name this error stack represents
  16741. * @var string
  16742. * @access protected
  16743. */
  16744. var $_package;
  16745. /**
  16746. * Determines whether a PEAR_Error is thrown upon every error addition
  16747. * @var boolean
  16748. * @access private
  16749. */
  16750. var $_compat = false;
  16751. /**
  16752. * If set to a valid callback, this will be used to generate the error
  16753. * message from the error code, otherwise the message passed in will be
  16754. * used
  16755. * @var false|string|array
  16756. * @access private
  16757. */
  16758. var $_msgCallback = false;
  16759. /**
  16760. * If set to a valid callback, this will be used to generate the error
  16761. * context for an error. For PHP-related errors, this will be a file
  16762. * and line number as retrieved from debug_backtrace(), but can be
  16763. * customized for other purposes. The error might actually be in a separate
  16764. * configuration file, or in a database query.
  16765. * @var false|string|array
  16766. * @access protected
  16767. */
  16768. var $_contextCallback = false;
  16769. /**
  16770. * If set to a valid callback, this will be called every time an error
  16771. * is pushed onto the stack. The return value will be used to determine
  16772. * whether to allow an error to be pushed or logged.
  16773. *
  16774. * The return value must be one an PEAR_ERRORSTACK_* constant
  16775. * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
  16776. * @var false|string|array
  16777. * @access protected
  16778. */
  16779. var $_errorCallback = array();
  16780. /**
  16781. * PEAR::Log object for logging errors
  16782. * @var false|Log
  16783. * @access protected
  16784. */
  16785. var $_logger = false;
  16786. /**
  16787. * Error messages - designed to be overridden
  16788. * @var array
  16789. * @abstract
  16790. */
  16791. var $_errorMsgs = array();
  16792. /**
  16793. * Set up a new error stack
  16794. *
  16795. * @param string $package name of the package this error stack represents
  16796. * @param callback $msgCallback callback used for error message generation
  16797. * @param callback $contextCallback callback used for context generation,
  16798. * defaults to {@link getFileLine()}
  16799. * @param boolean $throwPEAR_Error
  16800. */
  16801. function __construct($package, $msgCallback = false, $contextCallback = false,
  16802. $throwPEAR_Error = false)
  16803. {
  16804. $this->_package = $package;
  16805. $this->setMessageCallback($msgCallback);
  16806. $this->setContextCallback($contextCallback);
  16807. $this->_compat = $throwPEAR_Error;
  16808. }
  16809. /**
  16810. * Return a single error stack for this package.
  16811. *
  16812. * Note that all parameters are ignored if the stack for package $package
  16813. * has already been instantiated
  16814. * @param string $package name of the package this error stack represents
  16815. * @param callback $msgCallback callback used for error message generation
  16816. * @param callback $contextCallback callback used for context generation,
  16817. * defaults to {@link getFileLine()}
  16818. * @param boolean $throwPEAR_Error
  16819. * @param string $stackClass class to instantiate
  16820. *
  16821. * @return PEAR_ErrorStack
  16822. */
  16823. public static function &singleton(
  16824. $package, $msgCallback = false, $contextCallback = false,
  16825. $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack'
  16826. ) {
  16827. if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
  16828. return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
  16829. }
  16830. if (!class_exists($stackClass)) {
  16831. if (function_exists('debug_backtrace')) {
  16832. $trace = debug_backtrace();
  16833. }
  16834. PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS,
  16835. 'exception', array('stackclass' => $stackClass),
  16836. 'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)',
  16837. false, $trace);
  16838. }
  16839. $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] =
  16840. new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error);
  16841. return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
  16842. }
  16843. /**
  16844. * Internal error handler for PEAR_ErrorStack class
  16845. *
  16846. * Dies if the error is an exception (and would have died anyway)
  16847. * @access private
  16848. */
  16849. function _handleError($err)
  16850. {
  16851. if ($err['level'] == 'exception') {
  16852. $message = $err['message'];
  16853. if (isset($_SERVER['REQUEST_URI'])) {
  16854. echo '<br />';
  16855. } else {
  16856. echo "\n";
  16857. }
  16858. var_dump($err['context']);
  16859. die($message);
  16860. }
  16861. }
  16862. /**
  16863. * Set up a PEAR::Log object for all error stacks that don't have one
  16864. * @param Log $log
  16865. */
  16866. public static function setDefaultLogger(&$log)
  16867. {
  16868. if (is_object($log) && method_exists($log, 'log') ) {
  16869. $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
  16870. } elseif (is_callable($log)) {
  16871. $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
  16872. }
  16873. }
  16874. /**
  16875. * Set up a PEAR::Log object for this error stack
  16876. * @param Log $log
  16877. */
  16878. function setLogger(&$log)
  16879. {
  16880. if (is_object($log) && method_exists($log, 'log') ) {
  16881. $this->_logger = &$log;
  16882. } elseif (is_callable($log)) {
  16883. $this->_logger = &$log;
  16884. }
  16885. }
  16886. /**
  16887. * Set an error code => error message mapping callback
  16888. *
  16889. * This method sets the callback that can be used to generate error
  16890. * messages for any instance
  16891. * @param array|string Callback function/method
  16892. */
  16893. function setMessageCallback($msgCallback)
  16894. {
  16895. if (!$msgCallback) {
  16896. $this->_msgCallback = array(&$this, 'getErrorMessage');
  16897. } else {
  16898. if (is_callable($msgCallback)) {
  16899. $this->_msgCallback = $msgCallback;
  16900. }
  16901. }
  16902. }
  16903. /**
  16904. * Get an error code => error message mapping callback
  16905. *
  16906. * This method returns the current callback that can be used to generate error
  16907. * messages
  16908. * @return array|string|false Callback function/method or false if none
  16909. */
  16910. function getMessageCallback()
  16911. {
  16912. return $this->_msgCallback;
  16913. }
  16914. /**
  16915. * Sets a default callback to be used by all error stacks
  16916. *
  16917. * This method sets the callback that can be used to generate error
  16918. * messages for a singleton
  16919. * @param array|string Callback function/method
  16920. * @param string Package name, or false for all packages
  16921. */
  16922. public static function setDefaultCallback($callback = false, $package = false)
  16923. {
  16924. if (!is_callable($callback)) {
  16925. $callback = false;
  16926. }
  16927. $package = $package ? $package : '*';
  16928. $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback;
  16929. }
  16930. /**
  16931. * Set a callback that generates context information (location of error) for an error stack
  16932. *
  16933. * This method sets the callback that can be used to generate context
  16934. * information for an error. Passing in NULL will disable context generation
  16935. * and remove the expensive call to debug_backtrace()
  16936. * @param array|string|null Callback function/method
  16937. */
  16938. function setContextCallback($contextCallback)
  16939. {
  16940. if ($contextCallback === null) {
  16941. return $this->_contextCallback = false;
  16942. }
  16943. if (!$contextCallback) {
  16944. $this->_contextCallback = array(&$this, 'getFileLine');
  16945. } else {
  16946. if (is_callable($contextCallback)) {
  16947. $this->_contextCallback = $contextCallback;
  16948. }
  16949. }
  16950. }
  16951. /**
  16952. * Set an error Callback
  16953. * If set to a valid callback, this will be called every time an error
  16954. * is pushed onto the stack. The return value will be used to determine
  16955. * whether to allow an error to be pushed or logged.
  16956. *
  16957. * The return value must be one of the ERRORSTACK_* constants.
  16958. *
  16959. * This functionality can be used to emulate PEAR's pushErrorHandling, and
  16960. * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of
  16961. * the error stack or logging
  16962. * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
  16963. * @see popCallback()
  16964. * @param string|array $cb
  16965. */
  16966. function pushCallback($cb)
  16967. {
  16968. array_push($this->_errorCallback, $cb);
  16969. }
  16970. /**
  16971. * Remove a callback from the error callback stack
  16972. * @see pushCallback()
  16973. * @return array|string|false
  16974. */
  16975. function popCallback()
  16976. {
  16977. if (!count($this->_errorCallback)) {
  16978. return false;
  16979. }
  16980. return array_pop($this->_errorCallback);
  16981. }
  16982. /**
  16983. * Set a temporary overriding error callback for every package error stack
  16984. *
  16985. * Use this to temporarily disable all existing callbacks (can be used
  16986. * to emulate the @ operator, for instance)
  16987. * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
  16988. * @see staticPopCallback(), pushCallback()
  16989. * @param string|array $cb
  16990. */
  16991. public static function staticPushCallback($cb)
  16992. {
  16993. array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb);
  16994. }
  16995. /**
  16996. * Remove a temporary overriding error callback
  16997. * @see staticPushCallback()
  16998. * @return array|string|false
  16999. */
  17000. public static function staticPopCallback()
  17001. {
  17002. $ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']);
  17003. if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) {
  17004. $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
  17005. }
  17006. return $ret;
  17007. }
  17008. /**
  17009. * Add an error to the stack
  17010. *
  17011. * If the message generator exists, it is called with 2 parameters.
  17012. * - the current Error Stack object
  17013. * - an array that is in the same format as an error. Available indices
  17014. * are 'code', 'package', 'time', 'params', 'level', and 'context'
  17015. *
  17016. * Next, if the error should contain context information, this is
  17017. * handled by the context grabbing method.
  17018. * Finally, the error is pushed onto the proper error stack
  17019. * @param int $code Package-specific error code
  17020. * @param string $level Error level. This is NOT spell-checked
  17021. * @param array $params associative array of error parameters
  17022. * @param string $msg Error message, or a portion of it if the message
  17023. * is to be generated
  17024. * @param array $repackage If this error re-packages an error pushed by
  17025. * another package, place the array returned from
  17026. * {@link pop()} in this parameter
  17027. * @param array $backtrace Protected parameter: use this to pass in the
  17028. * {@link debug_backtrace()} that should be used
  17029. * to find error context
  17030. * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also
  17031. * thrown. If a PEAR_Error is returned, the userinfo
  17032. * property is set to the following array:
  17033. *
  17034. * <code>
  17035. * array(
  17036. * 'code' => $code,
  17037. * 'params' => $params,
  17038. * 'package' => $this->_package,
  17039. * 'level' => $level,
  17040. * 'time' => time(),
  17041. * 'context' => $context,
  17042. * 'message' => $msg,
  17043. * //['repackage' => $err] repackaged error array/Exception class
  17044. * );
  17045. * </code>
  17046. *
  17047. * Normally, the previous array is returned.
  17048. */
  17049. function push($code, $level = 'error', $params = array(), $msg = false,
  17050. $repackage = false, $backtrace = false)
  17051. {
  17052. $context = false;
  17053. // grab error context
  17054. if ($this->_contextCallback) {
  17055. if (!$backtrace) {
  17056. $backtrace = debug_backtrace();
  17057. }
  17058. $context = call_user_func($this->_contextCallback, $code, $params, $backtrace);
  17059. }
  17060. // save error
  17061. $time = explode(' ', microtime());
  17062. $time = $time[1] + $time[0];
  17063. $err = array(
  17064. 'code' => $code,
  17065. 'params' => $params,
  17066. 'package' => $this->_package,
  17067. 'level' => $level,
  17068. 'time' => $time,
  17069. 'context' => $context,
  17070. 'message' => $msg,
  17071. );
  17072. if ($repackage) {
  17073. $err['repackage'] = $repackage;
  17074. }
  17075. // set up the error message, if necessary
  17076. if ($this->_msgCallback) {
  17077. $msg = call_user_func_array($this->_msgCallback,
  17078. array(&$this, $err));
  17079. $err['message'] = $msg;
  17080. }
  17081. $push = $log = true;
  17082. $die = false;
  17083. // try the overriding callback first
  17084. $callback = $this->staticPopCallback();
  17085. if ($callback) {
  17086. $this->staticPushCallback($callback);
  17087. }
  17088. if (!is_callable($callback)) {
  17089. // try the local callback next
  17090. $callback = $this->popCallback();
  17091. if (is_callable($callback)) {
  17092. $this->pushCallback($callback);
  17093. } else {
  17094. // try the default callback
  17095. $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ?
  17096. $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] :
  17097. $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*'];
  17098. }
  17099. }
  17100. if (is_callable($callback)) {
  17101. switch(call_user_func($callback, $err)){
  17102. case PEAR_ERRORSTACK_IGNORE:
  17103. return $err;
  17104. break;
  17105. case PEAR_ERRORSTACK_PUSH:
  17106. $log = false;
  17107. break;
  17108. case PEAR_ERRORSTACK_LOG:
  17109. $push = false;
  17110. break;
  17111. case PEAR_ERRORSTACK_DIE:
  17112. $die = true;
  17113. break;
  17114. // anything else returned has the same effect as pushandlog
  17115. }
  17116. }
  17117. if ($push) {
  17118. array_unshift($this->_errors, $err);
  17119. if (!isset($this->_errorsByLevel[$err['level']])) {
  17120. $this->_errorsByLevel[$err['level']] = array();
  17121. }
  17122. $this->_errorsByLevel[$err['level']][] = &$this->_errors[0];
  17123. }
  17124. if ($log) {
  17125. if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) {
  17126. $this->_log($err);
  17127. }
  17128. }
  17129. if ($die) {
  17130. die();
  17131. }
  17132. if ($this->_compat && $push) {
  17133. return $this->raiseError($msg, $code, null, null, $err);
  17134. }
  17135. return $err;
  17136. }
  17137. /**
  17138. * Static version of {@link push()}
  17139. *
  17140. * @param string $package Package name this error belongs to
  17141. * @param int $code Package-specific error code
  17142. * @param string $level Error level. This is NOT spell-checked
  17143. * @param array $params associative array of error parameters
  17144. * @param string $msg Error message, or a portion of it if the message
  17145. * is to be generated
  17146. * @param array $repackage If this error re-packages an error pushed by
  17147. * another package, place the array returned from
  17148. * {@link pop()} in this parameter
  17149. * @param array $backtrace Protected parameter: use this to pass in the
  17150. * {@link debug_backtrace()} that should be used
  17151. * to find error context
  17152. * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also
  17153. * thrown. see docs for {@link push()}
  17154. */
  17155. public static function staticPush(
  17156. $package, $code, $level = 'error', $params = array(),
  17157. $msg = false, $repackage = false, $backtrace = false
  17158. ) {
  17159. $s = &PEAR_ErrorStack::singleton($package);
  17160. if ($s->_contextCallback) {
  17161. if (!$backtrace) {
  17162. if (function_exists('debug_backtrace')) {
  17163. $backtrace = debug_backtrace();
  17164. }
  17165. }
  17166. }
  17167. return $s->push($code, $level, $params, $msg, $repackage, $backtrace);
  17168. }
  17169. /**
  17170. * Log an error using PEAR::Log
  17171. * @param array $err Error array
  17172. * @param array $levels Error level => Log constant map
  17173. * @access protected
  17174. */
  17175. function _log($err)
  17176. {
  17177. if ($this->_logger) {
  17178. $logger = &$this->_logger;
  17179. } else {
  17180. $logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'];
  17181. }
  17182. if (is_a($logger, 'Log')) {
  17183. $levels = array(
  17184. 'exception' => PEAR_LOG_CRIT,
  17185. 'alert' => PEAR_LOG_ALERT,
  17186. 'critical' => PEAR_LOG_CRIT,
  17187. 'error' => PEAR_LOG_ERR,
  17188. 'warning' => PEAR_LOG_WARNING,
  17189. 'notice' => PEAR_LOG_NOTICE,
  17190. 'info' => PEAR_LOG_INFO,
  17191. 'debug' => PEAR_LOG_DEBUG);
  17192. if (isset($levels[$err['level']])) {
  17193. $level = $levels[$err['level']];
  17194. } else {
  17195. $level = PEAR_LOG_INFO;
  17196. }
  17197. $logger->log($err['message'], $level, $err);
  17198. } else { // support non-standard logs
  17199. call_user_func($logger, $err);
  17200. }
  17201. }
  17202. /**
  17203. * Pop an error off of the error stack
  17204. *
  17205. * @return false|array
  17206. * @since 0.4alpha it is no longer possible to specify a specific error
  17207. * level to return - the last error pushed will be returned, instead
  17208. */
  17209. function pop()
  17210. {
  17211. $err = @array_shift($this->_errors);
  17212. if (!is_null($err)) {
  17213. @array_pop($this->_errorsByLevel[$err['level']]);
  17214. if (!count($this->_errorsByLevel[$err['level']])) {
  17215. unset($this->_errorsByLevel[$err['level']]);
  17216. }
  17217. }
  17218. return $err;
  17219. }
  17220. /**
  17221. * Pop an error off of the error stack, static method
  17222. *
  17223. * @param string package name
  17224. * @return boolean
  17225. * @since PEAR1.5.0a1
  17226. */
  17227. static function staticPop($package)
  17228. {
  17229. if ($package) {
  17230. if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
  17231. return false;
  17232. }
  17233. return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->pop();
  17234. }
  17235. }
  17236. /**
  17237. * Determine whether there are any errors on the stack
  17238. * @param string|array Level name. Use to determine if any errors
  17239. * of level (string), or levels (array) have been pushed
  17240. * @return boolean
  17241. */
  17242. function hasErrors($level = false)
  17243. {
  17244. if ($level) {
  17245. return isset($this->_errorsByLevel[$level]);
  17246. }
  17247. return count($this->_errors);
  17248. }
  17249. /**
  17250. * Retrieve all errors since last purge
  17251. *
  17252. * @param boolean set in order to empty the error stack
  17253. * @param string level name, to return only errors of a particular severity
  17254. * @return array
  17255. */
  17256. function getErrors($purge = false, $level = false)
  17257. {
  17258. if (!$purge) {
  17259. if ($level) {
  17260. if (!isset($this->_errorsByLevel[$level])) {
  17261. return array();
  17262. } else {
  17263. return $this->_errorsByLevel[$level];
  17264. }
  17265. } else {
  17266. return $this->_errors;
  17267. }
  17268. }
  17269. if ($level) {
  17270. $ret = $this->_errorsByLevel[$level];
  17271. foreach ($this->_errorsByLevel[$level] as $i => $unused) {
  17272. // entries are references to the $_errors array
  17273. $this->_errorsByLevel[$level][$i] = false;
  17274. }
  17275. // array_filter removes all entries === false
  17276. $this->_errors = array_filter($this->_errors);
  17277. unset($this->_errorsByLevel[$level]);
  17278. return $ret;
  17279. }
  17280. $ret = $this->_errors;
  17281. $this->_errors = array();
  17282. $this->_errorsByLevel = array();
  17283. return $ret;
  17284. }
  17285. /**
  17286. * Determine whether there are any errors on a single error stack, or on any error stack
  17287. *
  17288. * The optional parameter can be used to test the existence of any errors without the need of
  17289. * singleton instantiation
  17290. * @param string|false Package name to check for errors
  17291. * @param string Level name to check for a particular severity
  17292. * @return boolean
  17293. */
  17294. public static function staticHasErrors($package = false, $level = false)
  17295. {
  17296. if ($package) {
  17297. if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
  17298. return false;
  17299. }
  17300. return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level);
  17301. }
  17302. foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
  17303. if ($obj->hasErrors($level)) {
  17304. return true;
  17305. }
  17306. }
  17307. return false;
  17308. }
  17309. /**
  17310. * Get a list of all errors since last purge, organized by package
  17311. * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be
  17312. * @param boolean $purge Set to purge the error stack of existing errors
  17313. * @param string $level Set to a level name in order to retrieve only errors of a particular level
  17314. * @param boolean $merge Set to return a flat array, not organized by package
  17315. * @param array $sortfunc Function used to sort a merged array - default
  17316. * sorts by time, and should be good for most cases
  17317. *
  17318. * @return array
  17319. */
  17320. public static function staticGetErrors(
  17321. $purge = false, $level = false, $merge = false,
  17322. $sortfunc = array('PEAR_ErrorStack', '_sortErrors')
  17323. ) {
  17324. $ret = array();
  17325. if (!is_callable($sortfunc)) {
  17326. $sortfunc = array('PEAR_ErrorStack', '_sortErrors');
  17327. }
  17328. foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
  17329. $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level);
  17330. if ($test) {
  17331. if ($merge) {
  17332. $ret = array_merge($ret, $test);
  17333. } else {
  17334. $ret[$package] = $test;
  17335. }
  17336. }
  17337. }
  17338. if ($merge) {
  17339. usort($ret, $sortfunc);
  17340. }
  17341. return $ret;
  17342. }
  17343. /**
  17344. * Error sorting function, sorts by time
  17345. * @access private
  17346. */
  17347. public static function _sortErrors($a, $b)
  17348. {
  17349. if ($a['time'] == $b['time']) {
  17350. return 0;
  17351. }
  17352. if ($a['time'] < $b['time']) {
  17353. return 1;
  17354. }
  17355. return -1;
  17356. }
  17357. /**
  17358. * Standard file/line number/function/class context callback
  17359. *
  17360. * This function uses a backtrace generated from {@link debug_backtrace()}
  17361. * and so will not work at all in PHP < 4.3.0. The frame should
  17362. * reference the frame that contains the source of the error.
  17363. * @return array|false either array('file' => file, 'line' => line,
  17364. * 'function' => function name, 'class' => class name) or
  17365. * if this doesn't work, then false
  17366. * @param unused
  17367. * @param integer backtrace frame.
  17368. * @param array Results of debug_backtrace()
  17369. */
  17370. public static function getFileLine($code, $params, $backtrace = null)
  17371. {
  17372. if ($backtrace === null) {
  17373. return false;
  17374. }
  17375. $frame = 0;
  17376. $functionframe = 1;
  17377. if (!isset($backtrace[1])) {
  17378. $functionframe = 0;
  17379. } else {
  17380. while (isset($backtrace[$functionframe]['function']) &&
  17381. $backtrace[$functionframe]['function'] == 'eval' &&
  17382. isset($backtrace[$functionframe + 1])) {
  17383. $functionframe++;
  17384. }
  17385. }
  17386. if (isset($backtrace[$frame])) {
  17387. if (!isset($backtrace[$frame]['file'])) {
  17388. $frame++;
  17389. }
  17390. $funcbacktrace = $backtrace[$functionframe];
  17391. $filebacktrace = $backtrace[$frame];
  17392. $ret = array('file' => $filebacktrace['file'],
  17393. 'line' => $filebacktrace['line']);
  17394. // rearrange for eval'd code or create function errors
  17395. if (strpos($filebacktrace['file'], '(') &&
  17396. preg_match(';^(.*?)\((\d+)\) : (.*?)\\z;', $filebacktrace['file'],
  17397. $matches)) {
  17398. $ret['file'] = $matches[1];
  17399. $ret['line'] = $matches[2] + 0;
  17400. }
  17401. if (isset($funcbacktrace['function']) && isset($backtrace[1])) {
  17402. if ($funcbacktrace['function'] != 'eval') {
  17403. if ($funcbacktrace['function'] == '__lambda_func') {
  17404. $ret['function'] = 'create_function() code';
  17405. } else {
  17406. $ret['function'] = $funcbacktrace['function'];
  17407. }
  17408. }
  17409. }
  17410. if (isset($funcbacktrace['class']) && isset($backtrace[1])) {
  17411. $ret['class'] = $funcbacktrace['class'];
  17412. }
  17413. return $ret;
  17414. }
  17415. return false;
  17416. }
  17417. /**
  17418. * Standard error message generation callback
  17419. *
  17420. * This method may also be called by a custom error message generator
  17421. * to fill in template values from the params array, simply
  17422. * set the third parameter to the error message template string to use
  17423. *
  17424. * The special variable %__msg% is reserved: use it only to specify
  17425. * where a message passed in by the user should be placed in the template,
  17426. * like so:
  17427. *
  17428. * Error message: %msg% - internal error
  17429. *
  17430. * If the message passed like so:
  17431. *
  17432. * <code>
  17433. * $stack->push(ERROR_CODE, 'error', array(), 'server error 500');
  17434. * </code>
  17435. *
  17436. * The returned error message will be "Error message: server error 500 -
  17437. * internal error"
  17438. * @param PEAR_ErrorStack
  17439. * @param array
  17440. * @param string|false Pre-generated error message template
  17441. *
  17442. * @return string
  17443. */
  17444. public static function getErrorMessage(&$stack, $err, $template = false)
  17445. {
  17446. if ($template) {
  17447. $mainmsg = $template;
  17448. } else {
  17449. $mainmsg = $stack->getErrorMessageTemplate($err['code']);
  17450. }
  17451. $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg);
  17452. if (is_array($err['params']) && count($err['params'])) {
  17453. foreach ($err['params'] as $name => $val) {
  17454. if (is_array($val)) {
  17455. // @ is needed in case $val is a multi-dimensional array
  17456. $val = @implode(', ', $val);
  17457. }
  17458. if (is_object($val)) {
  17459. if (method_exists($val, '__toString')) {
  17460. $val = $val->__toString();
  17461. } else {
  17462. PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING,
  17463. 'warning', array('obj' => get_class($val)),
  17464. 'object %obj% passed into getErrorMessage, but has no __toString() method');
  17465. $val = 'Object';
  17466. }
  17467. }
  17468. $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg);
  17469. }
  17470. }
  17471. return $mainmsg;
  17472. }
  17473. /**
  17474. * Standard Error Message Template generator from code
  17475. * @return string
  17476. */
  17477. function getErrorMessageTemplate($code)
  17478. {
  17479. if (!isset($this->_errorMsgs[$code])) {
  17480. return '%__msg%';
  17481. }
  17482. return $this->_errorMsgs[$code];
  17483. }
  17484. /**
  17485. * Set the Error Message Template array
  17486. *
  17487. * The array format must be:
  17488. * <pre>
  17489. * array(error code => 'message template',...)
  17490. * </pre>
  17491. *
  17492. * Error message parameters passed into {@link push()} will be used as input
  17493. * for the error message. If the template is 'message %foo% was %bar%', and the
  17494. * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will
  17495. * be 'message one was six'
  17496. * @return string
  17497. */
  17498. function setErrorMessageTemplate($template)
  17499. {
  17500. $this->_errorMsgs = $template;
  17501. }
  17502. /**
  17503. * emulate PEAR::raiseError()
  17504. *
  17505. * @return PEAR_Error
  17506. */
  17507. function raiseError()
  17508. {
  17509. require_once 'phar://go-pear.phar/' . 'PEAR.php';
  17510. $args = func_get_args();
  17511. return call_user_func_array(array('PEAR', 'raiseError'), $args);
  17512. }
  17513. }
  17514. $stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack');
  17515. $stack->pushCallback(array('PEAR_ErrorStack', '_handleError'));
  17516. ?>
  17517. <?php
  17518. /**
  17519. * PEAR_Frontend, the singleton-based frontend for user input/output
  17520. *
  17521. * PHP versions 4 and 5
  17522. *
  17523. * @category pear
  17524. * @package PEAR
  17525. * @author Greg Beaver <cellog@php.net>
  17526. * @copyright 1997-2009 The Authors
  17527. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  17528. * @link http://pear.php.net/package/PEAR
  17529. * @since File available since Release 1.4.0a1
  17530. */
  17531. /**
  17532. * Include error handling
  17533. */
  17534. //require_once 'PEAR.php';
  17535. /**
  17536. * Which user interface class is being used.
  17537. * @var string class name
  17538. */
  17539. $GLOBALS['_PEAR_FRONTEND_CLASS'] = 'PEAR_Frontend_CLI';
  17540. /**
  17541. * Instance of $_PEAR_Command_uiclass.
  17542. * @var object
  17543. */
  17544. $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = null;
  17545. /**
  17546. * Singleton-based frontend for PEAR user input/output
  17547. *
  17548. * @category pear
  17549. * @package PEAR
  17550. * @author Greg Beaver <cellog@php.net>
  17551. * @copyright 1997-2009 The Authors
  17552. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  17553. * @version Release: 1.10.16
  17554. * @link http://pear.php.net/package/PEAR
  17555. * @since Class available since Release 1.4.0a1
  17556. */
  17557. class PEAR_Frontend extends PEAR
  17558. {
  17559. /**
  17560. * Retrieve the frontend object
  17561. * @return PEAR_Frontend_CLI|PEAR_Frontend_Web|PEAR_Frontend_Gtk
  17562. */
  17563. public static function &singleton($type = null)
  17564. {
  17565. if ($type === null) {
  17566. if (!isset($GLOBALS['_PEAR_FRONTEND_SINGLETON'])) {
  17567. $a = false;
  17568. return $a;
  17569. }
  17570. return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
  17571. }
  17572. $a = PEAR_Frontend::setFrontendClass($type);
  17573. return $a;
  17574. }
  17575. /**
  17576. * Set the frontend class that will be used by calls to {@link singleton()}
  17577. *
  17578. * Frontends are expected to conform to the PEAR naming standard of
  17579. * _ => DIRECTORY_SEPARATOR (PEAR_Frontend_CLI is in PEAR/Frontend/CLI.php)
  17580. * @param string $uiclass full class name
  17581. * @return PEAR_Frontend
  17582. */
  17583. public static function &setFrontendClass($uiclass)
  17584. {
  17585. if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) &&
  17586. is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], $uiclass)) {
  17587. return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
  17588. }
  17589. if (!class_exists($uiclass)) {
  17590. $file = 'phar://go-pear.phar/' . str_replace('_', '/', $uiclass) . '.php';
  17591. if (PEAR_Frontend::isIncludeable($file)) {
  17592. include_once $file;
  17593. }
  17594. }
  17595. if (class_exists($uiclass)) {
  17596. $obj = new $uiclass;
  17597. // quick test to see if this class implements a few of the most
  17598. // important frontend methods
  17599. if (is_a($obj, 'PEAR_Frontend')) {
  17600. $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$obj;
  17601. $GLOBALS['_PEAR_FRONTEND_CLASS'] = $uiclass;
  17602. return $obj;
  17603. }
  17604. $err = PEAR::raiseError("not a frontend class: $uiclass");
  17605. return $err;
  17606. }
  17607. $err = PEAR::raiseError("no such class: $uiclass");
  17608. return $err;
  17609. }
  17610. /**
  17611. * Set the frontend class that will be used by calls to {@link singleton()}
  17612. *
  17613. * Frontends are expected to be a descendant of PEAR_Frontend
  17614. * @param PEAR_Frontend
  17615. * @return PEAR_Frontend
  17616. */
  17617. public static function &setFrontendObject($uiobject)
  17618. {
  17619. if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) &&
  17620. is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], get_class($uiobject))) {
  17621. return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
  17622. }
  17623. if (!is_a($uiobject, 'PEAR_Frontend')) {
  17624. $err = PEAR::raiseError('not a valid frontend class: (' .
  17625. get_class($uiobject) . ')');
  17626. return $err;
  17627. }
  17628. $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$uiobject;
  17629. $GLOBALS['_PEAR_FRONTEND_CLASS'] = get_class($uiobject);
  17630. return $uiobject;
  17631. }
  17632. /**
  17633. * @param string $path relative or absolute include path
  17634. * @return boolean
  17635. */
  17636. public static function isIncludeable($path)
  17637. {
  17638. if (file_exists($path) && is_readable($path)) {
  17639. return true;
  17640. }
  17641. $fp = @fopen($path, 'r', true);
  17642. if ($fp) {
  17643. fclose($fp);
  17644. return true;
  17645. }
  17646. return false;
  17647. }
  17648. /**
  17649. * @param PEAR_Config
  17650. */
  17651. function setConfig(&$config)
  17652. {
  17653. }
  17654. /**
  17655. * This can be overridden to allow session-based temporary file management
  17656. *
  17657. * By default, all files are deleted at the end of a session. The web installer
  17658. * needs to be able to sustain a list over many sessions in order to support
  17659. * user interaction with install scripts
  17660. */
  17661. static function addTempFile($file)
  17662. {
  17663. $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
  17664. }
  17665. /**
  17666. * Log an action
  17667. *
  17668. * @param string $msg the message to log
  17669. * @param boolean $append_crlf
  17670. * @return boolean true
  17671. * @abstract
  17672. */
  17673. function log($msg, $append_crlf = true)
  17674. {
  17675. }
  17676. /**
  17677. * Run a post-installation script
  17678. *
  17679. * @param array $scripts array of post-install scripts
  17680. * @abstract
  17681. */
  17682. function runPostinstallScripts(&$scripts)
  17683. {
  17684. }
  17685. /**
  17686. * Display human-friendly output formatted depending on the
  17687. * $command parameter.
  17688. *
  17689. * This should be able to handle basic output data with no command
  17690. * @param mixed $data data structure containing the information to display
  17691. * @param string $command command from which this method was called
  17692. * @abstract
  17693. */
  17694. function outputData($data, $command = '_default')
  17695. {
  17696. }
  17697. /**
  17698. * Display a modal form dialog and return the given input
  17699. *
  17700. * A frontend that requires multiple requests to retrieve and process
  17701. * data must take these needs into account, and implement the request
  17702. * handling code.
  17703. * @param string $command command from which this method was called
  17704. * @param array $prompts associative array. keys are the input field names
  17705. * and values are the description
  17706. * @param array $types array of input field types (text, password,
  17707. * etc.) keys have to be the same like in $prompts
  17708. * @param array $defaults array of default values. again keys have
  17709. * to be the same like in $prompts. Do not depend
  17710. * on a default value being set.
  17711. * @return array input sent by the user
  17712. * @abstract
  17713. */
  17714. function userDialog($command, $prompts, $types = array(), $defaults = array())
  17715. {
  17716. }
  17717. }
  17718. <?php
  17719. /**
  17720. * PEAR_Frontend_CLI
  17721. *
  17722. * PHP versions 4 and 5
  17723. *
  17724. * @category pear
  17725. * @package PEAR
  17726. * @author Stig Bakken <ssb@php.net>
  17727. * @author Greg Beaver <cellog@php.net>
  17728. * @copyright 1997-2009 The Authors
  17729. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  17730. * @link http://pear.php.net/package/PEAR
  17731. * @since File available since Release 0.1
  17732. */
  17733. /**
  17734. * base class
  17735. */
  17736. require_once 'phar://go-pear.phar/' . 'PEAR/Frontend.php';
  17737. /**
  17738. * Command-line Frontend for the PEAR Installer
  17739. * @category pear
  17740. * @package PEAR
  17741. * @author Stig Bakken <ssb@php.net>
  17742. * @author Greg Beaver <cellog@php.net>
  17743. * @copyright 1997-2009 The Authors
  17744. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  17745. * @version Release: 1.10.16
  17746. * @link http://pear.php.net/package/PEAR
  17747. * @since Class available since Release 0.1
  17748. */
  17749. class PEAR_Frontend_CLI extends PEAR_Frontend
  17750. {
  17751. /**
  17752. * What type of user interface this frontend is for.
  17753. * @var string
  17754. * @access public
  17755. */
  17756. var $type = 'CLI';
  17757. var $lp = ''; // line prefix
  17758. var $params = array();
  17759. var $term = array(
  17760. 'bold' => '',
  17761. 'normal' => '',
  17762. );
  17763. function __construct()
  17764. {
  17765. parent::__construct();
  17766. $term = getenv('TERM'); //(cox) $_ENV is empty for me in 4.1.1
  17767. if (function_exists('posix_isatty') && !posix_isatty(1)) {
  17768. // output is being redirected to a file or through a pipe
  17769. } elseif ($term) {
  17770. if (preg_match('/^(xterm|vt220|linux)/', $term)) {
  17771. $this->term['bold'] = sprintf("%c%c%c%c", 27, 91, 49, 109);
  17772. $this->term['normal'] = sprintf("%c%c%c", 27, 91, 109);
  17773. } elseif (preg_match('/^vt100/', $term)) {
  17774. $this->term['bold'] = sprintf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0);
  17775. $this->term['normal'] = sprintf("%c%c%c%c%c", 27, 91, 109, 0, 0);
  17776. }
  17777. } elseif (OS_WINDOWS) {
  17778. // XXX add ANSI codes here
  17779. }
  17780. }
  17781. /**
  17782. * @param object PEAR_Error object
  17783. */
  17784. function displayError($e)
  17785. {
  17786. return $this->_displayLine($e->getMessage());
  17787. }
  17788. /**
  17789. * @param object PEAR_Error object
  17790. */
  17791. function displayFatalError($eobj)
  17792. {
  17793. $this->displayError($eobj);
  17794. if (class_exists('PEAR_Config')) {
  17795. $config = &PEAR_Config::singleton();
  17796. if ($config->get('verbose') > 5) {
  17797. if (function_exists('debug_print_backtrace')) {
  17798. debug_print_backtrace();
  17799. exit(1);
  17800. }
  17801. $raised = false;
  17802. foreach (debug_backtrace() as $i => $frame) {
  17803. if (!$raised) {
  17804. if (isset($frame['class'])
  17805. && strtolower($frame['class']) == 'pear'
  17806. && strtolower($frame['function']) == 'raiseerror'
  17807. ) {
  17808. $raised = true;
  17809. } else {
  17810. continue;
  17811. }
  17812. }
  17813. $frame['class'] = !isset($frame['class']) ? '' : $frame['class'];
  17814. $frame['type'] = !isset($frame['type']) ? '' : $frame['type'];
  17815. $frame['function'] = !isset($frame['function']) ? '' : $frame['function'];
  17816. $frame['line'] = !isset($frame['line']) ? '' : $frame['line'];
  17817. $this->_displayLine("#$i: $frame[class]$frame[type]$frame[function] $frame[line]");
  17818. }
  17819. }
  17820. }
  17821. exit(1);
  17822. }
  17823. /**
  17824. * Instruct the runInstallScript method to skip a paramgroup that matches the
  17825. * id value passed in.
  17826. *
  17827. * This method is useful for dynamically configuring which sections of a post-install script
  17828. * will be run based on the user's setup, which is very useful for making flexible
  17829. * post-install scripts without losing the cross-Frontend ability to retrieve user input
  17830. * @param string
  17831. */
  17832. function skipParamgroup($id)
  17833. {
  17834. $this->_skipSections[$id] = true;
  17835. }
  17836. function runPostinstallScripts(&$scripts)
  17837. {
  17838. foreach ($scripts as $i => $script) {
  17839. $this->runInstallScript($scripts[$i]->_params, $scripts[$i]->_obj);
  17840. }
  17841. }
  17842. /**
  17843. * @param array $xml contents of postinstallscript tag
  17844. * @param object $script post-installation script
  17845. * @param string install|upgrade
  17846. */
  17847. function runInstallScript($xml, &$script)
  17848. {
  17849. $this->_skipSections = array();
  17850. if (!is_array($xml) || !isset($xml['paramgroup'])) {
  17851. $script->run(array(), '_default');
  17852. return;
  17853. }
  17854. $completedPhases = array();
  17855. if (!isset($xml['paramgroup'][0])) {
  17856. $xml['paramgroup'] = array($xml['paramgroup']);
  17857. }
  17858. foreach ($xml['paramgroup'] as $group) {
  17859. if (isset($this->_skipSections[$group['id']])) {
  17860. // the post-install script chose to skip this section dynamically
  17861. continue;
  17862. }
  17863. if (isset($group['name'])) {
  17864. $paramname = explode('::', $group['name']);
  17865. if ($lastgroup['id'] != $paramname[0]) {
  17866. continue;
  17867. }
  17868. $group['name'] = $paramname[1];
  17869. if (!isset($answers)) {
  17870. return;
  17871. }
  17872. if (isset($answers[$group['name']])) {
  17873. switch ($group['conditiontype']) {
  17874. case '=' :
  17875. if ($answers[$group['name']] != $group['value']) {
  17876. continue 2;
  17877. }
  17878. break;
  17879. case '!=' :
  17880. if ($answers[$group['name']] == $group['value']) {
  17881. continue 2;
  17882. }
  17883. break;
  17884. case 'preg_match' :
  17885. if (!@preg_match('/' . $group['value'] . '/',
  17886. $answers[$group['name']])) {
  17887. continue 2;
  17888. }
  17889. break;
  17890. default :
  17891. return;
  17892. }
  17893. }
  17894. }
  17895. $lastgroup = $group;
  17896. if (isset($group['instructions'])) {
  17897. $this->_display($group['instructions']);
  17898. }
  17899. if (!isset($group['param'][0])) {
  17900. $group['param'] = array($group['param']);
  17901. }
  17902. if (isset($group['param'])) {
  17903. if (method_exists($script, 'postProcessPrompts')) {
  17904. $prompts = $script->postProcessPrompts($group['param'], $group['id']);
  17905. if (!is_array($prompts) || count($prompts) != count($group['param'])) {
  17906. $this->outputData('postinstall', 'Error: post-install script did not ' .
  17907. 'return proper post-processed prompts');
  17908. $prompts = $group['param'];
  17909. } else {
  17910. foreach ($prompts as $i => $var) {
  17911. if (!is_array($var) || !isset($var['prompt']) ||
  17912. !isset($var['name']) ||
  17913. ($var['name'] != $group['param'][$i]['name']) ||
  17914. ($var['type'] != $group['param'][$i]['type'])
  17915. ) {
  17916. $this->outputData('postinstall', 'Error: post-install script ' .
  17917. 'modified the variables or prompts, severe security risk. ' .
  17918. 'Will instead use the defaults from the package.xml');
  17919. $prompts = $group['param'];
  17920. }
  17921. }
  17922. }
  17923. $answers = $this->confirmDialog($prompts);
  17924. } else {
  17925. $answers = $this->confirmDialog($group['param']);
  17926. }
  17927. }
  17928. if ((isset($answers) && $answers) || !isset($group['param'])) {
  17929. if (!isset($answers)) {
  17930. $answers = array();
  17931. }
  17932. array_unshift($completedPhases, $group['id']);
  17933. if (!$script->run($answers, $group['id'])) {
  17934. $script->run($completedPhases, '_undoOnError');
  17935. return;
  17936. }
  17937. } else {
  17938. $script->run($completedPhases, '_undoOnError');
  17939. return;
  17940. }
  17941. }
  17942. }
  17943. /**
  17944. * Ask for user input, confirm the answers and continue until the user is satisfied
  17945. * @param array an array of arrays, format array('name' => 'paramname', 'prompt' =>
  17946. * 'text to display', 'type' => 'string'[, default => 'default value'])
  17947. * @return array
  17948. */
  17949. function confirmDialog($params)
  17950. {
  17951. $answers = $prompts = $types = array();
  17952. foreach ($params as $param) {
  17953. $prompts[$param['name']] = $param['prompt'];
  17954. $types[$param['name']] = $param['type'];
  17955. $answers[$param['name']] = isset($param['default']) ? $param['default'] : '';
  17956. }
  17957. $tried = false;
  17958. do {
  17959. if ($tried) {
  17960. $i = 1;
  17961. foreach ($answers as $var => $value) {
  17962. if (!strlen($value)) {
  17963. echo $this->bold("* Enter an answer for #" . $i . ": ({$prompts[$var]})\n");
  17964. }
  17965. $i++;
  17966. }
  17967. }
  17968. $answers = $this->userDialog('', $prompts, $types, $answers);
  17969. $tried = true;
  17970. } while (is_array($answers) && count(array_filter($answers)) != count($prompts));
  17971. return $answers;
  17972. }
  17973. function userDialog($command, $prompts, $types = array(), $defaults = array(), $screensize = 20)
  17974. {
  17975. if (!is_array($prompts)) {
  17976. return array();
  17977. }
  17978. $testprompts = array_keys($prompts);
  17979. $result = $defaults;
  17980. reset($prompts);
  17981. if (count($prompts) === 1) {
  17982. foreach ($prompts as $key => $prompt) {
  17983. $type = $types[$key];
  17984. $default = @$defaults[$key];
  17985. print "$prompt ";
  17986. if ($default) {
  17987. print "[$default] ";
  17988. }
  17989. print ": ";
  17990. $line = fgets(STDIN, 2048);
  17991. $result[$key] = ($default && trim($line) == '') ? $default : trim($line);
  17992. }
  17993. return $result;
  17994. }
  17995. $first_run = true;
  17996. while (true) {
  17997. $descLength = max(array_map('strlen', $prompts));
  17998. $descFormat = "%-{$descLength}s";
  17999. $last = count($prompts);
  18000. $i = 0;
  18001. foreach ($prompts as $n => $var) {
  18002. $res = isset($result[$n]) ? $result[$n] : null;
  18003. printf("%2d. $descFormat : %s\n", ++$i, $prompts[$n], $res);
  18004. }
  18005. print "\n1-$last, 'all', 'abort', or Enter to continue: ";
  18006. $tmp = trim(fgets(STDIN, 1024));
  18007. if (empty($tmp)) {
  18008. break;
  18009. }
  18010. if ($tmp == 'abort') {
  18011. return false;
  18012. }
  18013. if (isset($testprompts[(int)$tmp - 1])) {
  18014. $var = $testprompts[(int)$tmp - 1];
  18015. $desc = $prompts[$var];
  18016. $current = @$result[$var];
  18017. print "$desc [$current] : ";
  18018. $tmp = trim(fgets(STDIN, 1024));
  18019. if ($tmp !== '') {
  18020. $result[$var] = $tmp;
  18021. }
  18022. } elseif ($tmp == 'all') {
  18023. foreach ($prompts as $var => $desc) {
  18024. $current = $result[$var];
  18025. print "$desc [$current] : ";
  18026. $tmp = trim(fgets(STDIN, 1024));
  18027. if (trim($tmp) !== '') {
  18028. $result[$var] = trim($tmp);
  18029. }
  18030. }
  18031. }
  18032. $first_run = false;
  18033. }
  18034. return $result;
  18035. }
  18036. function userConfirm($prompt, $default = 'yes')
  18037. {
  18038. trigger_error("PEAR_Frontend_CLI::userConfirm not yet converted", E_USER_ERROR);
  18039. static $positives = array('y', 'yes', 'on', '1');
  18040. static $negatives = array('n', 'no', 'off', '0');
  18041. print "$this->lp$prompt [$default] : ";
  18042. $fp = fopen("php://stdin", "r");
  18043. $line = fgets($fp, 2048);
  18044. fclose($fp);
  18045. $answer = strtolower(trim($line));
  18046. if (empty($answer)) {
  18047. $answer = $default;
  18048. }
  18049. if (in_array($answer, $positives)) {
  18050. return true;
  18051. }
  18052. if (in_array($answer, $negatives)) {
  18053. return false;
  18054. }
  18055. if (in_array($default, $positives)) {
  18056. return true;
  18057. }
  18058. return false;
  18059. }
  18060. function outputData($data, $command = '_default')
  18061. {
  18062. switch ($command) {
  18063. case 'channel-info':
  18064. foreach ($data as $type => $section) {
  18065. if ($type == 'main') {
  18066. $section['data'] = array_values($section['data']);
  18067. }
  18068. $this->outputData($section);
  18069. }
  18070. break;
  18071. case 'install':
  18072. case 'upgrade':
  18073. case 'upgrade-all':
  18074. if (is_array($data) && isset($data['release_warnings'])) {
  18075. $this->_displayLine('');
  18076. $this->_startTable(array(
  18077. 'border' => false,
  18078. 'caption' => 'Release Warnings'
  18079. ));
  18080. $this->_tableRow(array($data['release_warnings']), null, array(1 => array('wrap' => 55)));
  18081. $this->_endTable();
  18082. $this->_displayLine('');
  18083. }
  18084. $this->_displayLine(is_array($data) ? $data['data'] : $data);
  18085. break;
  18086. case 'search':
  18087. $this->_startTable($data);
  18088. if (isset($data['headline']) && is_array($data['headline'])) {
  18089. $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
  18090. }
  18091. $packages = array();
  18092. foreach($data['data'] as $category) {
  18093. foreach($category as $name => $pkg) {
  18094. $packages[$pkg[0]] = $pkg;
  18095. }
  18096. }
  18097. $p = array_keys($packages);
  18098. natcasesort($p);
  18099. foreach ($p as $name) {
  18100. $this->_tableRow($packages[$name], null, array(1 => array('wrap' => 55)));
  18101. }
  18102. $this->_endTable();
  18103. break;
  18104. case 'list-all':
  18105. if (!isset($data['data'])) {
  18106. $this->_displayLine('No packages in channel');
  18107. break;
  18108. }
  18109. $this->_startTable($data);
  18110. if (isset($data['headline']) && is_array($data['headline'])) {
  18111. $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
  18112. }
  18113. $packages = array();
  18114. foreach($data['data'] as $category) {
  18115. foreach($category as $name => $pkg) {
  18116. $packages[$pkg[0]] = $pkg;
  18117. }
  18118. }
  18119. $p = array_keys($packages);
  18120. natcasesort($p);
  18121. foreach ($p as $name) {
  18122. $pkg = $packages[$name];
  18123. unset($pkg[4], $pkg[5]);
  18124. $this->_tableRow($pkg, null, array(1 => array('wrap' => 55)));
  18125. }
  18126. $this->_endTable();
  18127. break;
  18128. case 'config-show':
  18129. $data['border'] = false;
  18130. $opts = array(
  18131. 0 => array('wrap' => 30),
  18132. 1 => array('wrap' => 20),
  18133. 2 => array('wrap' => 35)
  18134. );
  18135. $this->_startTable($data);
  18136. if (isset($data['headline']) && is_array($data['headline'])) {
  18137. $this->_tableRow($data['headline'], array('bold' => true), $opts);
  18138. }
  18139. foreach ($data['data'] as $group) {
  18140. foreach ($group as $value) {
  18141. if ($value[2] == '') {
  18142. $value[2] = "<not set>";
  18143. }
  18144. $this->_tableRow($value, null, $opts);
  18145. }
  18146. }
  18147. $this->_endTable();
  18148. break;
  18149. case 'remote-info':
  18150. $d = $data;
  18151. $data = array(
  18152. 'caption' => 'Package details:',
  18153. 'border' => false,
  18154. 'data' => array(
  18155. array("Latest", $data['stable']),
  18156. array("Installed", $data['installed']),
  18157. array("Package", $data['name']),
  18158. array("License", $data['license']),
  18159. array("Category", $data['category']),
  18160. array("Summary", $data['summary']),
  18161. array("Description", $data['description']),
  18162. ),
  18163. );
  18164. if (isset($d['deprecated']) && $d['deprecated']) {
  18165. $conf = &PEAR_Config::singleton();
  18166. $reg = $conf->getRegistry();
  18167. $name = $reg->parsedPackageNameToString($d['deprecated'], true);
  18168. $data['data'][] = array('Deprecated! use', $name);
  18169. }
  18170. default: {
  18171. if (is_array($data)) {
  18172. $this->_startTable($data);
  18173. $count = count($data['data'][0]);
  18174. if ($count == 2) {
  18175. $opts = array(0 => array('wrap' => 25),
  18176. 1 => array('wrap' => 48)
  18177. );
  18178. } elseif ($count == 3) {
  18179. $opts = array(0 => array('wrap' => 30),
  18180. 1 => array('wrap' => 20),
  18181. 2 => array('wrap' => 35)
  18182. );
  18183. } else {
  18184. $opts = null;
  18185. }
  18186. if (isset($data['headline']) && is_array($data['headline'])) {
  18187. $this->_tableRow($data['headline'],
  18188. array('bold' => true),
  18189. $opts);
  18190. }
  18191. if (is_array($data['data'])) {
  18192. foreach($data['data'] as $row) {
  18193. $this->_tableRow($row, null, $opts);
  18194. }
  18195. } else {
  18196. $this->_tableRow(array($data['data']), null, $opts);
  18197. }
  18198. $this->_endTable();
  18199. } else {
  18200. $this->_displayLine($data);
  18201. }
  18202. }
  18203. }
  18204. }
  18205. function log($text, $append_crlf = true)
  18206. {
  18207. if ($append_crlf) {
  18208. return $this->_displayLine($text);
  18209. }
  18210. return $this->_display($text);
  18211. }
  18212. function bold($text)
  18213. {
  18214. if (empty($this->term['bold'])) {
  18215. return strtoupper($text);
  18216. }
  18217. return $this->term['bold'] . $text . $this->term['normal'];
  18218. }
  18219. function _displayHeading($title)
  18220. {
  18221. print $this->lp.$this->bold($title)."\n";
  18222. print $this->lp.str_repeat("=", strlen($title))."\n";
  18223. }
  18224. function _startTable($params = array())
  18225. {
  18226. $params['table_data'] = array();
  18227. $params['widest'] = array(); // indexed by column
  18228. $params['highest'] = array(); // indexed by row
  18229. $params['ncols'] = 0;
  18230. $this->params = $params;
  18231. }
  18232. function _tableRow($columns, $rowparams = array(), $colparams = array())
  18233. {
  18234. $highest = 1;
  18235. for ($i = 0; $i < count($columns); $i++) {
  18236. $col = &$columns[$i];
  18237. if (isset($colparams[$i]) && !empty($colparams[$i]['wrap'])) {
  18238. $col = wordwrap($col, $colparams[$i]['wrap']);
  18239. }
  18240. if (strpos($col, "\n") !== false) {
  18241. $multiline = explode("\n", $col);
  18242. $w = 0;
  18243. foreach ($multiline as $n => $line) {
  18244. $len = strlen($line);
  18245. if ($len > $w) {
  18246. $w = $len;
  18247. }
  18248. }
  18249. $lines = count($multiline);
  18250. } else {
  18251. $w = strlen($col);
  18252. }
  18253. if (isset($this->params['widest'][$i])) {
  18254. if ($w > $this->params['widest'][$i]) {
  18255. $this->params['widest'][$i] = $w;
  18256. }
  18257. } else {
  18258. $this->params['widest'][$i] = $w;
  18259. }
  18260. $tmp = count_chars($columns[$i], 1);
  18261. // handle unix, mac and windows formats
  18262. $lines = (isset($tmp[10]) ? $tmp[10] : (isset($tmp[13]) ? $tmp[13] : 0)) + 1;
  18263. if ($lines > $highest) {
  18264. $highest = $lines;
  18265. }
  18266. }
  18267. if (count($columns) > $this->params['ncols']) {
  18268. $this->params['ncols'] = count($columns);
  18269. }
  18270. $new_row = array(
  18271. 'data' => $columns,
  18272. 'height' => $highest,
  18273. 'rowparams' => $rowparams,
  18274. 'colparams' => $colparams,
  18275. );
  18276. $this->params['table_data'][] = $new_row;
  18277. }
  18278. function _endTable()
  18279. {
  18280. extract($this->params);
  18281. if (!empty($caption)) {
  18282. $this->_displayHeading($caption);
  18283. }
  18284. if (count($table_data) === 0) {
  18285. return;
  18286. }
  18287. if (!isset($width)) {
  18288. $width = $widest;
  18289. } else {
  18290. for ($i = 0; $i < $ncols; $i++) {
  18291. if (!isset($width[$i])) {
  18292. $width[$i] = $widest[$i];
  18293. }
  18294. }
  18295. }
  18296. $border = false;
  18297. if (empty($border)) {
  18298. $cellstart = '';
  18299. $cellend = ' ';
  18300. $rowend = '';
  18301. $padrowend = false;
  18302. $borderline = '';
  18303. } else {
  18304. $cellstart = '| ';
  18305. $cellend = ' ';
  18306. $rowend = '|';
  18307. $padrowend = true;
  18308. $borderline = '+';
  18309. foreach ($width as $w) {
  18310. $borderline .= str_repeat('-', $w + strlen($cellstart) + strlen($cellend) - 1);
  18311. $borderline .= '+';
  18312. }
  18313. }
  18314. if ($borderline) {
  18315. $this->_displayLine($borderline);
  18316. }
  18317. for ($i = 0; $i < count($table_data); $i++) {
  18318. extract($table_data[$i]);
  18319. if (!is_array($rowparams)) {
  18320. $rowparams = array();
  18321. }
  18322. if (!is_array($colparams)) {
  18323. $colparams = array();
  18324. }
  18325. $rowlines = array();
  18326. if ($height > 1) {
  18327. for ($c = 0; $c < count($data); $c++) {
  18328. $rowlines[$c] = preg_split('/(\r?\n|\r)/', $data[$c]);
  18329. if (count($rowlines[$c]) < $height) {
  18330. $rowlines[$c] = array_pad($rowlines[$c], $height, '');
  18331. }
  18332. }
  18333. } else {
  18334. for ($c = 0; $c < count($data); $c++) {
  18335. $rowlines[$c] = array($data[$c]);
  18336. }
  18337. }
  18338. for ($r = 0; $r < $height; $r++) {
  18339. $rowtext = '';
  18340. for ($c = 0; $c < count($data); $c++) {
  18341. if (isset($colparams[$c])) {
  18342. $attribs = array_merge($rowparams, $colparams);
  18343. } else {
  18344. $attribs = $rowparams;
  18345. }
  18346. $w = isset($width[$c]) ? $width[$c] : 0;
  18347. //$cell = $data[$c];
  18348. $cell = $rowlines[$c][$r];
  18349. $l = strlen($cell);
  18350. if ($l > $w) {
  18351. $cell = substr($cell, 0, $w);
  18352. }
  18353. if (isset($attribs['bold'])) {
  18354. $cell = $this->bold($cell);
  18355. }
  18356. if ($l < $w) {
  18357. // not using str_pad here because we may
  18358. // add bold escape characters to $cell
  18359. $cell .= str_repeat(' ', $w - $l);
  18360. }
  18361. $rowtext .= $cellstart . $cell . $cellend;
  18362. }
  18363. if (!$border) {
  18364. $rowtext = rtrim($rowtext);
  18365. }
  18366. $rowtext .= $rowend;
  18367. $this->_displayLine($rowtext);
  18368. }
  18369. }
  18370. if ($borderline) {
  18371. $this->_displayLine($borderline);
  18372. }
  18373. }
  18374. function _displayLine($text)
  18375. {
  18376. print "$this->lp$text\n";
  18377. }
  18378. function _display($text)
  18379. {
  18380. print $text;
  18381. }
  18382. }
  18383. package.xml�����������������������������������������������������������������������������������������0000664�0001750�0001750�00000050350�14575343342�013062� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?>
  18384. <package packagerversion="1.10.14" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
  18385. <name>Archive_Tar</name>
  18386. <channel>pear.php.net</channel>
  18387. <summary>Tar file management class</summary>
  18388. <description>This class provides handling of tar files in PHP.
  18389. It supports creating, listing, extracting and adding to tar files.
  18390. Gzip support is available if PHP has the zlib extension built-in or
  18391. loaded. Bz2 compression is also supported with the bz2 extension loaded.
  18392. Also Lzma2 compressed archives are supported with xz extension.</description>
  18393. <lead>
  18394. <name>Vincent Blavet</name>
  18395. <user>vblavet</user>
  18396. <email>vincent@phpconcept.net</email>
  18397. <active>no</active>
  18398. </lead>
  18399. <lead>
  18400. <name>Greg Beaver</name>
  18401. <user>cellog</user>
  18402. <email>greg@chiaraquartet.net</email>
  18403. <active>no</active>
  18404. </lead>
  18405. <lead>
  18406. <name>Michiel Rook</name>
  18407. <user>mrook</user>
  18408. <email>mrook@php.net</email>
  18409. <active>no</active>
  18410. </lead>
  18411. <lead>
  18412. <name>Drew Webber</name>
  18413. <user>mcdruid</user>
  18414. <email>drew@mcdruid.co.uk</email>
  18415. <active>yes</active>
  18416. </lead>
  18417. <helper>
  18418. <name>Stig Bakken</name>
  18419. <user>ssb</user>
  18420. <email>stig@php.net</email>
  18421. <active>no</active>
  18422. </helper>
  18423. <date>2024-03-16</date>
  18424. <time>16:20:50</time>
  18425. <version>
  18426. <release>1.5.0</release>
  18427. <api>1.5.0</api>
  18428. </version>
  18429. <stability>
  18430. <release>stable</release>
  18431. <api>stable</api>
  18432. </stability>
  18433. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18434. <notes>
  18435. * PR #42: fix @return true... to @return bool true... on some functions
  18436. * PR #46: use 775 default for mkdirs, to avoid world-write
  18437. </notes>
  18438. <contents>
  18439. <dir name="/">
  18440. <file baseinstalldir="/" md5sum="3c8b402ca4f63adae90f662f1a1c1c9e" name="Archive/Tar.php" role="php" />
  18441. <file baseinstalldir="/" md5sum="2fb90f0be7089a45c09a0d1182792419" name="docs/Archive_Tar.txt" role="doc" />
  18442. </dir>
  18443. </contents>
  18444. <compatible>
  18445. <name>PEAR</name>
  18446. <channel>pear.php.net</channel>
  18447. <min>1.8.0</min>
  18448. <max>1.10.10</max>
  18449. </compatible>
  18450. <dependencies>
  18451. <required>
  18452. <php>
  18453. <min>5.2.0</min>
  18454. </php>
  18455. <pearinstaller>
  18456. <min>1.9.0</min>
  18457. </pearinstaller>
  18458. </required>
  18459. </dependencies>
  18460. <phprelease />
  18461. <changelog>
  18462. <release>
  18463. <version>
  18464. <release>1.5.0</release>
  18465. <api>1.5.0</api>
  18466. </version>
  18467. <stability>
  18468. <release>stable</release>
  18469. <api>stable</api>
  18470. </stability>
  18471. <date>2024-03-16</date>
  18472. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18473. <notes>
  18474. * PR #42: fix @return true... to @return bool true... on some functions
  18475. * PR #46: use 775 default for mkdirs, to avoid world-write
  18476. </notes>
  18477. </release>
  18478. <release>
  18479. <version>
  18480. <release>1.4.14</release>
  18481. <api>1.4.0</api>
  18482. </version>
  18483. <stability>
  18484. <release>stable</release>
  18485. <api>stable</api>
  18486. </stability>
  18487. <date>2021-02-16</date>
  18488. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18489. <notes>
  18490. * Properly fix symbolic link path traversal (CVE-2021-32610)
  18491. </notes>
  18492. </release>
  18493. <release>
  18494. <version>
  18495. <release>1.4.13</release>
  18496. <api>1.4.0</api>
  18497. </version>
  18498. <stability>
  18499. <release>stable</release>
  18500. <api>stable</api>
  18501. </stability>
  18502. <date>2021-02-16</date>
  18503. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18504. <notes>
  18505. * Fix Bug #27010: Relative symlinks failing (out-of path file extraction) [mrook]
  18506. </notes>
  18507. </release>
  18508. <release>
  18509. <version>
  18510. <release>1.4.12</release>
  18511. <api>1.4.0</api>
  18512. </version>
  18513. <stability>
  18514. <release>stable</release>
  18515. <api>stable</api>
  18516. </stability>
  18517. <date>2021-01-18</date>
  18518. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18519. <notes>
  18520. * Fix Bug #27008: Symlink out-of-path write vulnerability (CVE-2020-36193) [mrook]
  18521. </notes>
  18522. </release>
  18523. <release>
  18524. <version>
  18525. <release>1.4.11</release>
  18526. <api>1.4.0</api>
  18527. </version>
  18528. <stability>
  18529. <release>stable</release>
  18530. <api>stable</api>
  18531. </stability>
  18532. <date>2020-11-19</date>
  18533. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18534. <notes>
  18535. * Fix Bug #27002: Filename manipulation vulnerabilities (CVE-2020-28948 / CVE-2020-28949) [mrook]
  18536. </notes>
  18537. </release>
  18538. <release>
  18539. <version>
  18540. <release>1.4.10</release>
  18541. <api>1.4.0</api>
  18542. </version>
  18543. <stability>
  18544. <release>stable</release>
  18545. <api>stable</api>
  18546. </stability>
  18547. <date>2020-09-15</date>
  18548. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18549. <notes>
  18550. * Fix block padding when the file buffer length is a multiple of 512 and smaller than Archive_Tar buffer length
  18551. * Don&apos;t try to copy username/groupname in chroot jail
  18552. </notes>
  18553. </release>
  18554. <release>
  18555. <version>
  18556. <release>1.4.9</release>
  18557. <api>1.4.0</api>
  18558. </version>
  18559. <stability>
  18560. <release>stable</release>
  18561. <api>stable</api>
  18562. </stability>
  18563. <date>2019-12-04</date>
  18564. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18565. <notes>
  18566. * Implement Feature #23861: Add option to disallow symlinks [mrook]
  18567. </notes>
  18568. </release>
  18569. <release>
  18570. <version>
  18571. <release>1.4.8</release>
  18572. <api>1.4.0</api>
  18573. </version>
  18574. <stability>
  18575. <release>stable</release>
  18576. <api>stable</api>
  18577. </stability>
  18578. <date>2019-10-21</date>
  18579. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18580. <notes>
  18581. * Fix Bug #23852: PHP 7.4 - Archive_Tar-&gt;_readHeader throws deprecation [mrook]
  18582. </notes>
  18583. </release>
  18584. <release>
  18585. <version>
  18586. <release>1.4.7</release>
  18587. <api>1.4.0</api>
  18588. </version>
  18589. <stability>
  18590. <release>stable</release>
  18591. <api>stable</api>
  18592. </stability>
  18593. <date>2019-04-08</date>
  18594. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18595. <notes>
  18596. * Improved performance by increasing read buffer size
  18597. </notes>
  18598. </release>
  18599. <release>
  18600. <version>
  18601. <release>1.4.6</release>
  18602. <api>1.4.0</api>
  18603. </version>
  18604. <stability>
  18605. <release>stable</release>
  18606. <api>stable</api>
  18607. </stability>
  18608. <date>2019-02-01</date>
  18609. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18610. <notes>
  18611. * Improve path traversal detection for forward and backward slashes
  18612. </notes>
  18613. </release>
  18614. <release>
  18615. <version>
  18616. <release>1.4.5</release>
  18617. <api>1.4.0</api>
  18618. </version>
  18619. <stability>
  18620. <release>stable</release>
  18621. <api>stable</api>
  18622. </stability>
  18623. <date>2019-01-02</date>
  18624. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18625. <notes>
  18626. * Fix Bug #23788: Relative symlinks are broken [mrook]
  18627. </notes>
  18628. </release>
  18629. <release>
  18630. <version>
  18631. <release>1.4.4</release>
  18632. <api>1.4.0</api>
  18633. </version>
  18634. <stability>
  18635. <release>stable</release>
  18636. <api>stable</api>
  18637. </stability>
  18638. <date>2018-12-20</date>
  18639. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18640. <notes>
  18641. * Fix Bug #21058: Long symlinks are not supported [mrook]
  18642. * Fix Bug #23782: Prevent phar:// files from being extracted [mrook]
  18643. </notes>
  18644. </release>
  18645. <release>
  18646. <version>
  18647. <release>1.4.3</release>
  18648. <api>1.4.0</api>
  18649. </version>
  18650. <stability>
  18651. <release>stable</release>
  18652. <api>stable</api>
  18653. </stability>
  18654. <date>2017-06-11</date>
  18655. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18656. <notes>
  18657. * Fix Bug #21218: Cannot use result of built-in function in write context in PHP
  18658. 7.2.0alpha1 [mrook]
  18659. </notes>
  18660. </release>
  18661. <release>
  18662. <version>
  18663. <release>1.4.2</release>
  18664. <api>1.4.0</api>
  18665. </version>
  18666. <stability>
  18667. <release>stable</release>
  18668. <api>stable</api>
  18669. </stability>
  18670. <date>2016-02-25</date>
  18671. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18672. <notes>
  18673. * Fix reading of archives with files &gt; 8GB
  18674. * Performance optimizations
  18675. * Do not try to call require_once on PEAR.php if it has already been loaded by the autoloader
  18676. </notes>
  18677. </release>
  18678. <release>
  18679. <version>
  18680. <release>1.4.1</release>
  18681. <api>1.4.0</api>
  18682. </version>
  18683. <stability>
  18684. <release>stable</release>
  18685. <api>stable</api>
  18686. </stability>
  18687. <date>2015-08-05</date>
  18688. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18689. <notes>
  18690. * Update composer.json to use pear-core-minimal 1.10.0alpha2
  18691. </notes>
  18692. </release>
  18693. <release>
  18694. <version>
  18695. <release>1.4.0</release>
  18696. <api>1.4.0</api>
  18697. </version>
  18698. <stability>
  18699. <release>stable</release>
  18700. <api>stable</api>
  18701. </stability>
  18702. <date>2015-07-20</date>
  18703. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18704. <notes>
  18705. * Add support for PHP 7
  18706. * Drop support for PHP 4
  18707. * Add visibility declarations to methods and properties
  18708. </notes>
  18709. </release>
  18710. <release>
  18711. <version>
  18712. <release>1.3.16</release>
  18713. <api>1.3.1</api>
  18714. </version>
  18715. <stability>
  18716. <release>stable</release>
  18717. <api>stable</api>
  18718. </stability>
  18719. <date>2015-04-14</date>
  18720. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18721. <notes>
  18722. * Fix Bug #20514: invalid package.xml; not installable with pyrus [mrook]
  18723. </notes>
  18724. </release>
  18725. <release>
  18726. <version>
  18727. <release>1.3.15</release>
  18728. <api>1.3.1</api>
  18729. </version>
  18730. <stability>
  18731. <release>stable</release>
  18732. <api>stable</api>
  18733. </stability>
  18734. <date>2015-03-05</date>
  18735. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18736. <notes>
  18737. * Fixes composer.json parse error
  18738. </notes>
  18739. </release>
  18740. <release>
  18741. <version>
  18742. <release>1.3.14</release>
  18743. <api>1.3.1</api>
  18744. </version>
  18745. <stability>
  18746. <release>stable</release>
  18747. <api>stable</api>
  18748. </stability>
  18749. <date>2015-02-26</date>
  18750. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18751. <notes>
  18752. * Fix Bug #18505: Possible incorrect handling of file names in TAR [mrook]
  18753. </notes>
  18754. </release>
  18755. <release>
  18756. <version>
  18757. <release>1.3.13</release>
  18758. <api>1.3.1</api>
  18759. </version>
  18760. <stability>
  18761. <release>stable</release>
  18762. <api>stable</api>
  18763. </stability>
  18764. <date>2014-09-02</date>
  18765. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD
  18766. License</license>
  18767. <notes>
  18768. * Fix Bug #20382: gzopen fix [mrook]
  18769. </notes>
  18770. </release>
  18771. <release>
  18772. <version>
  18773. <release>1.3.12</release>
  18774. <api>1.3.1</api>
  18775. </version>
  18776. <stability>
  18777. <release>stable</release>
  18778. <api>stable</api>
  18779. </stability>
  18780. <date>2014-08-04</date>
  18781. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD
  18782. License</license>
  18783. <notes>
  18784. * Fix Bug #19964: Memory leaking in Archive_Tar [mrook]
  18785. * Fix Bug #20246: Broken with php 5.5.9 [mrook]
  18786. * Fix Bug #20275: &quot;pax_global_header&quot; looks like a regular file
  18787. * [mrook]
  18788. * Implement Feature #19827: pass filename to _addFile function - downstream
  18789. * patch [mrook]
  18790. * Implement Feature #20132: Add custom mode/uid/gid to addString() [mrook]
  18791. </notes>
  18792. </release>
  18793. <release>
  18794. <version>
  18795. <release>1.3.11</release>
  18796. <api>1.3.1</api>
  18797. </version>
  18798. <stability>
  18799. <release>stable</release>
  18800. <api>stable</api>
  18801. </stability>
  18802. <date>2013-02-09</date>
  18803. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD
  18804. License</license>
  18805. <notes>
  18806. * Fix Bug #19746: Broken with PHP 5.5 [mrook]
  18807. * Implement Feature #11258: Custom date/time in files added on-the-fly
  18808. * [mrook]
  18809. </notes>
  18810. </release>
  18811. <release>
  18812. <version>
  18813. <release>1.3.10</release>
  18814. <api>1.3.1</api>
  18815. </version>
  18816. <stability>
  18817. <release>stable</release>
  18818. <api>stable</api>
  18819. </stability>
  18820. <date>2012-04-10</date>
  18821. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD
  18822. License</license>
  18823. <notes>
  18824. * Fix Bug #13361: Unable to add() some files (ex. mp3) [mrook]
  18825. * Fix Bug #19330: Class creates incorrect (non-readable) tar.gz file
  18826. * [mrook]
  18827. </notes>
  18828. </release>
  18829. <release>
  18830. <version>
  18831. <release>1.3.9</release>
  18832. <api>1.3.1</api>
  18833. </version>
  18834. <stability>
  18835. <release>stable</release>
  18836. <api>stable</api>
  18837. </stability>
  18838. <date>2012-02-27</date>
  18839. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18840. <notes>
  18841. * Fix Bug #16759: No error thrown from missing PHP zlib functions [mrook]
  18842. * Fix Bug #18877: Incorrect handling of backslashes in filenames on Linux [mrook]
  18843. * Fix Bug #19085: Error while packaging [mrook]
  18844. * Fix Bug #19289: Invalid tar file generated [mrook]
  18845. </notes>
  18846. </release>
  18847. <release>
  18848. <version>
  18849. <release>1.3.8</release>
  18850. <api>1.3.1</api>
  18851. </version>
  18852. <stability>
  18853. <release>stable</release>
  18854. <api>stable</api>
  18855. </stability>
  18856. <date>2011-10-14</date>
  18857. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18858. <notes>
  18859. * Fix Bug #17853: Test failure: dirtraversal.phpt [mrook]
  18860. * Fix Bug #18512: dead links are not saved in tar file [mrook]
  18861. * Fix Bug #18702: Unpacks incorrectly on long file names using header prefix [mrook]
  18862. * Implement Feature #10145: Patch to return a Pear Error Object on failure [mrook]
  18863. * Implement Feature #17491: Option to preserve permissions [mrook]
  18864. * Implement Feature #17813: Prevent PHP notice when extracting corrupted archive [mrook]
  18865. </notes>
  18866. </release>
  18867. <release>
  18868. <version>
  18869. <release>1.3.7</release>
  18870. <api>1.3.1</api>
  18871. </version>
  18872. <stability>
  18873. <release>stable</release>
  18874. <api>stable</api>
  18875. </stability>
  18876. <date>2010-04-26</date>
  18877. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18878. <notes>
  18879. PEAR compatibility update
  18880. </notes>
  18881. </release>
  18882. <release>
  18883. <version>
  18884. <release>1.3.6</release>
  18885. <api>1.3.1</api>
  18886. </version>
  18887. <stability>
  18888. <release>stable</release>
  18889. <api>stable</api>
  18890. </stability>
  18891. <date>2010-03-09</date>
  18892. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18893. <notes>
  18894. * Fix Bug #16963: extractList can&apos;t extract zipped files from big tar [mrook]
  18895. * Implement Feature #4013: Ignoring files and directories on creating an archive. [mrook]
  18896. </notes>
  18897. </release>
  18898. <release>
  18899. <version>
  18900. <release>1.3.5</release>
  18901. <api>1.3.1</api>
  18902. </version>
  18903. <stability>
  18904. <release>stable</release>
  18905. <api>stable</api>
  18906. </stability>
  18907. <date>2009-12-31</date>
  18908. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18909. <notes>
  18910. * Fix Bug #16958: Update &apos;compatible&apos; tag in package.xml [mrook]
  18911. </notes>
  18912. </release>
  18913. <release>
  18914. <version>
  18915. <release>1.3.4</release>
  18916. <api>1.3.1</api>
  18917. </version>
  18918. <stability>
  18919. <release>stable</release>
  18920. <api>stable</api>
  18921. </stability>
  18922. <date>2009-12-30</date>
  18923. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18924. <notes>
  18925. * Fix Bug #11871: wrong result of ::listContent() if filename begins or ends with space [mrook]
  18926. * Fix Bug #12462: invalid tar magic [mrook]
  18927. * Fix Bug #13918: Long filenames may get up to 511 0x00 bytes appended on read [mrook]
  18928. * Fix Bug #16202: Bogus modification times [mrook]
  18929. * Implement Feature #16212: Die is not exception [mrook]
  18930. </notes>
  18931. </release>
  18932. <release>
  18933. <version>
  18934. <release>1.3.3</release>
  18935. <api>1.3.1</api>
  18936. </version>
  18937. <stability>
  18938. <release>stable</release>
  18939. <api>stable</api>
  18940. </stability>
  18941. <date>2009-03-27</date>
  18942. <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
  18943. <notes>
  18944. Change the license to New BSD license
  18945. minor bugfix release
  18946. * fix Bug #9921 compression with bzip2 fails [cellog]
  18947. * fix Bug #11594 _readLongHeader leaves 0 bytes in filename [jamessas]
  18948. * fix Bug #11769 Incorrect symlink handing [fajar99]
  18949. </notes>
  18950. </release>
  18951. <release>
  18952. <version>
  18953. <release>1.3.2</release>
  18954. <api>1.3.1</api>
  18955. </version>
  18956. <stability>
  18957. <release>stable</release>
  18958. <api>stable</api>
  18959. </stability>
  18960. <date>2007-01-03</date>
  18961. <license uri="http://www.php.net/license">PHP License</license>
  18962. <notes>
  18963. Correct Bug #4016
  18964. Remove duplicate remove error display with &apos;@&apos;
  18965. Correct Bug #3909 : Check existence of OS_WINDOWS constant
  18966. Correct Bug #5452 fix for &quot;lone zero block&quot; when untarring packages
  18967. Change filemode (from pear-core/Archive/Tar.php v.1.21)
  18968. Correct Bug #6486 Can not extract symlinks
  18969. Correct Bug #6933 Archive_Tar (Tar file management class) Directory traversal
  18970. Correct Bug #8114 Files added on-the-fly not storing date
  18971. Correct Bug #9352 Bug on _dirCheck function over nfs path
  18972. </notes>
  18973. </release>
  18974. <release>
  18975. <version>
  18976. <release>1.3.1</release>
  18977. <api>1.3.1</api>
  18978. </version>
  18979. <stability>
  18980. <release>stable</release>
  18981. <api>stable</api>
  18982. </stability>
  18983. <date>2005-03-17</date>
  18984. <license uri="http://www.php.net/license">PHP License</license>
  18985. <notes>
  18986. Correct Bug #3855
  18987. </notes>
  18988. </release>
  18989. <release>
  18990. <version>
  18991. <release>1.3.0</release>
  18992. <api>1.3.0</api>
  18993. </version>
  18994. <stability>
  18995. <release>stable</release>
  18996. <api>stable</api>
  18997. </stability>
  18998. <date>2005-03-06</date>
  18999. <license uri="http://www.php.net/license">PHP License</license>
  19000. <notes>
  19001. Bugs correction (2475, 2488, 2135, 2176)
  19002. </notes>
  19003. </release>
  19004. <release>
  19005. <version>
  19006. <release>1.2</release>
  19007. <api>1.2</api>
  19008. </version>
  19009. <stability>
  19010. <release>stable</release>
  19011. <api>stable</api>
  19012. </stability>
  19013. <date>2004-05-08</date>
  19014. <license uri="http://www.php.net/license">PHP License</license>
  19015. <notes>
  19016. Add support for other separator than the space char and bug
  19017. correction
  19018. </notes>
  19019. </release>
  19020. <release>
  19021. <version>
  19022. <release>1.1</release>
  19023. <api>1.1</api>
  19024. </version>
  19025. <stability>
  19026. <release>stable</release>
  19027. <api>stable</api>
  19028. </stability>
  19029. <date>2003-05-28</date>
  19030. <license uri="http://www.php.net/license">PHP License</license>
  19031. <notes>
  19032. * Add support for BZ2 compression
  19033. * Add support for add and extract without using temporary files : methods addString() and extractInString()
  19034. </notes>
  19035. </release>
  19036. <release>
  19037. <version>
  19038. <release>1.0</release>
  19039. <api>1.0</api>
  19040. </version>
  19041. <stability>
  19042. <release>stable</release>
  19043. <api>stable</api>
  19044. </stability>
  19045. <date>2003-01-24</date>
  19046. <license uri="http://www.php.net/license">PHP License</license>
  19047. <notes>
  19048. Change status to stable
  19049. </notes>
  19050. </release>
  19051. <release>
  19052. <version>
  19053. <release>0.10-b1</release>
  19054. <api>0.10-b1</api>
  19055. </version>
  19056. <stability>
  19057. <release>beta</release>
  19058. <api>beta</api>
  19059. </stability>
  19060. <date>2003-01-08</date>
  19061. <license uri="http://www.php.net/license">PHP License</license>
  19062. <notes>
  19063. Add support for long filenames (greater than 99 characters)
  19064. </notes>
  19065. </release>
  19066. <release>
  19067. <version>
  19068. <release>0.9</release>
  19069. <api>0.9</api>
  19070. </version>
  19071. <stability>
  19072. <release>stable</release>
  19073. <api>stable</api>
  19074. </stability>
  19075. <date>2002-05-27</date>
  19076. <license uri="http://www.php.net/license">PHP License</license>
  19077. <notes>
  19078. Auto-detect gzip&apos;ed files
  19079. </notes>
  19080. </release>
  19081. <release>
  19082. <version>
  19083. <release>0.4</release>
  19084. <api>0.4</api>
  19085. </version>
  19086. <stability>
  19087. <release>stable</release>
  19088. <api>stable</api>
  19089. </stability>
  19090. <date>2002-05-20</date>
  19091. <license uri="http://www.php.net/license">PHP License</license>
  19092. <notes>
  19093. Windows bugfix: use forward slashes inside archives
  19094. </notes>
  19095. </release>
  19096. <release>
  19097. <version>
  19098. <release>0.2</release>
  19099. <api>0.2</api>
  19100. </version>
  19101. <stability>
  19102. <release>stable</release>
  19103. <api>stable</api>
  19104. </stability>
  19105. <date>2002-02-18</date>
  19106. <license uri="http://www.php.net/license">PHP License</license>
  19107. <notes>
  19108. From initial commit to stable
  19109. </notes>
  19110. </release>
  19111. <release>
  19112. <version>
  19113. <release>0.3</release>
  19114. <api>0.3</api>
  19115. </version>
  19116. <stability>
  19117. <release>stable</release>
  19118. <api>stable</api>
  19119. </stability>
  19120. <date>2002-04-13</date>
  19121. <license uri="http://www.php.net/license">PHP License</license>
  19122. <notes>
  19123. Windows bugfix: used wrong directory separators
  19124. </notes>
  19125. </release>
  19126. </changelog>
  19127. </package>
  19128. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Archive_Tar-1.5.0/Archive/Tar.php�������������������������������������������������������������������0000664�0001750�0001750�00000254446�14575343342�016427� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  19129. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  19130. /**
  19131. * File::CSV
  19132. *
  19133. * PHP versions 4 and 5
  19134. *
  19135. * Copyright (c) 1997-2008,
  19136. * Vincent Blavet <vincent@phpconcept.net>
  19137. * All rights reserved.
  19138. *
  19139. * Redistribution and use in source and binary forms, with or without
  19140. * modification, are permitted provided that the following conditions are met:
  19141. *
  19142. * * Redistributions of source code must retain the above copyright notice,
  19143. * this list of conditions and the following disclaimer.
  19144. * * Redistributions in binary form must reproduce the above copyright
  19145. * notice, this list of conditions and the following disclaimer in the
  19146. * documentation and/or other materials provided with the distribution.
  19147. *
  19148. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  19149. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19150. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19151. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
  19152. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  19153. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  19154. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  19155. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  19156. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  19157. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  19158. *
  19159. * @category File_Formats
  19160. * @package Archive_Tar
  19161. * @author Vincent Blavet <vincent@phpconcept.net>
  19162. * @copyright 1997-2010 The Authors
  19163. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  19164. * @version CVS: $Id$
  19165. * @link http://pear.php.net/package/Archive_Tar
  19166. */
  19167. // If the PEAR class cannot be loaded via the autoloader,
  19168. // then try to require_once it from the PHP include path.
  19169. if (!class_exists('PEAR')) {
  19170. require_once 'PEAR.php';
  19171. }
  19172. define('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
  19173. define('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));
  19174. if (!function_exists('gzopen') && function_exists('gzopen64')) {
  19175. function gzopen($filename, $mode, $use_include_path = 0)
  19176. {
  19177. return gzopen64($filename, $mode, $use_include_path);
  19178. }
  19179. }
  19180. if (!function_exists('gztell') && function_exists('gztell64')) {
  19181. function gztell($zp)
  19182. {
  19183. return gztell64($zp);
  19184. }
  19185. }
  19186. if (!function_exists('gzseek') && function_exists('gzseek64')) {
  19187. function gzseek($zp, $offset, $whence = SEEK_SET)
  19188. {
  19189. return gzseek64($zp, $offset, $whence);
  19190. }
  19191. }
  19192. /**
  19193. * Creates a (compressed) Tar archive
  19194. *
  19195. * @package Archive_Tar
  19196. * @author Vincent Blavet <vincent@phpconcept.net>
  19197. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  19198. * @version $Revision$
  19199. */
  19200. class Archive_Tar extends PEAR
  19201. {
  19202. /**
  19203. * @var string Name of the Tar
  19204. */
  19205. public $_tarname = '';
  19206. /**
  19207. * @var boolean if true, the Tar file will be gzipped
  19208. */
  19209. public $_compress = false;
  19210. /**
  19211. * @var string Type of compression : 'none', 'gz', 'bz2' or 'lzma2'
  19212. */
  19213. public $_compress_type = 'none';
  19214. /**
  19215. * @var string Explode separator
  19216. */
  19217. public $_separator = ' ';
  19218. /**
  19219. * @var file descriptor
  19220. */
  19221. public $_file = 0;
  19222. /**
  19223. * @var string Local Tar name of a remote Tar (http:// or ftp://)
  19224. */
  19225. public $_temp_tarname = '';
  19226. /**
  19227. * @var string regular expression for ignoring files or directories
  19228. */
  19229. public $_ignore_regexp = '';
  19230. /**
  19231. * @var object PEAR_Error object
  19232. */
  19233. public $error_object = null;
  19234. /**
  19235. * Format for data extraction
  19236. *
  19237. * @var string
  19238. */
  19239. public $_fmt = '';
  19240. /**
  19241. * @var int Length of the read buffer in bytes
  19242. */
  19243. protected $buffer_length;
  19244. /**
  19245. * Archive_Tar Class constructor. This flavour of the constructor only
  19246. * declare a new Archive_Tar object, identifying it by the name of the
  19247. * tar file.
  19248. * If the compress argument is set the tar will be read or created as a
  19249. * gzip or bz2 compressed TAR file.
  19250. *
  19251. * @param string $p_tarname The name of the tar archive to create
  19252. * @param string $p_compress can be null, 'gz', 'bz2' or 'lzma2'. This
  19253. * parameter indicates if gzip, bz2 or lzma2 compression
  19254. * is required. For compatibility reason the
  19255. * boolean value 'true' means 'gz'.
  19256. * @param int $buffer_length Length of the read buffer in bytes
  19257. *
  19258. * @return bool
  19259. */
  19260. public function __construct($p_tarname, $p_compress = null, $buffer_length = 512)
  19261. {
  19262. parent::__construct();
  19263. $this->_compress = false;
  19264. $this->_compress_type = 'none';
  19265. if (($p_compress === null) || ($p_compress == '')) {
  19266. if (@file_exists($p_tarname)) {
  19267. if ($fp = @fopen($p_tarname, "rb")) {
  19268. // look for gzip magic cookie
  19269. $data = fread($fp, 2);
  19270. fclose($fp);
  19271. if ($data == "\37\213") {
  19272. $this->_compress = true;
  19273. $this->_compress_type = 'gz';
  19274. // No sure it's enought for a magic code ....
  19275. } elseif ($data == "BZ") {
  19276. $this->_compress = true;
  19277. $this->_compress_type = 'bz2';
  19278. } elseif (file_get_contents($p_tarname, false, null, 1, 4) == '7zXZ') {
  19279. $this->_compress = true;
  19280. $this->_compress_type = 'lzma2';
  19281. }
  19282. }
  19283. } else {
  19284. // probably a remote file or some file accessible
  19285. // through a stream interface
  19286. if (substr($p_tarname, -2) == 'gz') {
  19287. $this->_compress = true;
  19288. $this->_compress_type = 'gz';
  19289. } elseif ((substr($p_tarname, -3) == 'bz2') ||
  19290. (substr($p_tarname, -2) == 'bz')
  19291. ) {
  19292. $this->_compress = true;
  19293. $this->_compress_type = 'bz2';
  19294. } else {
  19295. if (substr($p_tarname, -2) == 'xz') {
  19296. $this->_compress = true;
  19297. $this->_compress_type = 'lzma2';
  19298. }
  19299. }
  19300. }
  19301. } else {
  19302. if (($p_compress === true) || ($p_compress == 'gz')) {
  19303. $this->_compress = true;
  19304. $this->_compress_type = 'gz';
  19305. } else {
  19306. if ($p_compress == 'bz2') {
  19307. $this->_compress = true;
  19308. $this->_compress_type = 'bz2';
  19309. } else {
  19310. if ($p_compress == 'lzma2') {
  19311. $this->_compress = true;
  19312. $this->_compress_type = 'lzma2';
  19313. } else {
  19314. $this->_error(
  19315. "Unsupported compression type '$p_compress'\n" .
  19316. "Supported types are 'gz', 'bz2' and 'lzma2'.\n"
  19317. );
  19318. return false;
  19319. }
  19320. }
  19321. }
  19322. }
  19323. $this->_tarname = $p_tarname;
  19324. if ($this->_compress) { // assert zlib or bz2 or xz extension support
  19325. if ($this->_compress_type == 'gz') {
  19326. $extname = 'zlib';
  19327. } else {
  19328. if ($this->_compress_type == 'bz2') {
  19329. $extname = 'bz2';
  19330. } else {
  19331. if ($this->_compress_type == 'lzma2') {
  19332. $extname = 'xz';
  19333. }
  19334. }
  19335. }
  19336. if (!extension_loaded($extname)) {
  19337. PEAR::loadExtension($extname);
  19338. }
  19339. if (!extension_loaded($extname)) {
  19340. $this->_error(
  19341. "The extension '$extname' couldn't be found.\n" .
  19342. "Please make sure your version of PHP was built " .
  19343. "with '$extname' support.\n"
  19344. );
  19345. return false;
  19346. }
  19347. }
  19348. if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) {
  19349. $this->_fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
  19350. "a8checksum/a1typeflag/a100link/a6magic/a2version/" .
  19351. "a32uname/a32gname/a8devmajor/a8devminor/a131prefix";
  19352. } else {
  19353. $this->_fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" .
  19354. "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" .
  19355. "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix";
  19356. }
  19357. $this->buffer_length = $buffer_length;
  19358. }
  19359. public function __destruct()
  19360. {
  19361. $this->_close();
  19362. // ----- Look for a local copy to delete
  19363. if ($this->_temp_tarname != '' && (bool) preg_match('/^tar[[:alnum:]]*\.tmp$/', $this->_temp_tarname)) {
  19364. @unlink($this->_temp_tarname);
  19365. }
  19366. }
  19367. /**
  19368. * This method creates the archive file and add the files / directories
  19369. * that are listed in $p_filelist.
  19370. * If a file with the same name exist and is writable, it is replaced
  19371. * by the new tar.
  19372. * The method return false and a PEAR error text.
  19373. * The $p_filelist parameter can be an array of string, each string
  19374. * representing a filename or a directory name with their path if
  19375. * needed. It can also be a single string with names separated by a
  19376. * single blank.
  19377. * For each directory added in the archive, the files and
  19378. * sub-directories are also added.
  19379. * See also createModify() method for more details.
  19380. *
  19381. * @param array $p_filelist An array of filenames and directory names, or a
  19382. * single string with names separated by a single
  19383. * blank space.
  19384. *
  19385. * @return bool true on success, false on error.
  19386. * @see createModify()
  19387. */
  19388. public function create($p_filelist)
  19389. {
  19390. return $this->createModify($p_filelist, '', '');
  19391. }
  19392. /**
  19393. * This method add the files / directories that are listed in $p_filelist in
  19394. * the archive. If the archive does not exist it is created.
  19395. * The method return false and a PEAR error text.
  19396. * The files and directories listed are only added at the end of the archive,
  19397. * even if a file with the same name is already archived.
  19398. * See also createModify() method for more details.
  19399. *
  19400. * @param array $p_filelist An array of filenames and directory names, or a
  19401. * single string with names separated by a single
  19402. * blank space.
  19403. *
  19404. * @return bool true on success, false on error.
  19405. * @see createModify()
  19406. * @access public
  19407. */
  19408. public function add($p_filelist)
  19409. {
  19410. return $this->addModify($p_filelist, '', '');
  19411. }
  19412. /**
  19413. * @param string $p_path
  19414. * @param bool $p_preserve
  19415. * @param bool $p_symlinks
  19416. * @return bool
  19417. */
  19418. public function extract($p_path = '', $p_preserve = false, $p_symlinks = true)
  19419. {
  19420. return $this->extractModify($p_path, '', $p_preserve, $p_symlinks);
  19421. }
  19422. /**
  19423. * @return array|int
  19424. */
  19425. public function listContent()
  19426. {
  19427. $v_list_detail = array();
  19428. if ($this->_openRead()) {
  19429. if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
  19430. unset($v_list_detail);
  19431. $v_list_detail = 0;
  19432. }
  19433. $this->_close();
  19434. }
  19435. return $v_list_detail;
  19436. }
  19437. /**
  19438. * This method creates the archive file and add the files / directories
  19439. * that are listed in $p_filelist.
  19440. * If the file already exists and is writable, it is replaced by the
  19441. * new tar. It is a create and not an add. If the file exists and is
  19442. * read-only or is a directory it is not replaced. The method return
  19443. * false and a PEAR error text.
  19444. * The $p_filelist parameter can be an array of string, each string
  19445. * representing a filename or a directory name with their path if
  19446. * needed. It can also be a single string with names separated by a
  19447. * single blank.
  19448. * The path indicated in $p_remove_dir will be removed from the
  19449. * memorized path of each file / directory listed when this path
  19450. * exists. By default nothing is removed (empty path '')
  19451. * The path indicated in $p_add_dir will be added at the beginning of
  19452. * the memorized path of each file / directory listed. However it can
  19453. * be set to empty ''. The adding of a path is done after the removing
  19454. * of path.
  19455. * The path add/remove ability enables the user to prepare an archive
  19456. * for extraction in a different path than the origin files are.
  19457. * See also addModify() method for file adding properties.
  19458. *
  19459. * @param array $p_filelist An array of filenames and directory names,
  19460. * or a single string with names separated by
  19461. * a single blank space.
  19462. * @param string $p_add_dir A string which contains a path to be added
  19463. * to the memorized path of each element in
  19464. * the list.
  19465. * @param string $p_remove_dir A string which contains a path to be
  19466. * removed from the memorized path of each
  19467. * element in the list, when relevant.
  19468. *
  19469. * @return boolean true on success, false on error.
  19470. * @see addModify()
  19471. */
  19472. public function createModify($p_filelist, $p_add_dir, $p_remove_dir = '')
  19473. {
  19474. $v_result = true;
  19475. if (!$this->_openWrite()) {
  19476. return false;
  19477. }
  19478. if ($p_filelist != '') {
  19479. if (is_array($p_filelist)) {
  19480. $v_list = $p_filelist;
  19481. } elseif (is_string($p_filelist)) {
  19482. $v_list = explode($this->_separator, $p_filelist);
  19483. } else {
  19484. $this->_cleanFile();
  19485. $this->_error('Invalid file list');
  19486. return false;
  19487. }
  19488. $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
  19489. }
  19490. if ($v_result) {
  19491. $this->_writeFooter();
  19492. $this->_close();
  19493. } else {
  19494. $this->_cleanFile();
  19495. }
  19496. return $v_result;
  19497. }
  19498. /**
  19499. * This method add the files / directories listed in $p_filelist at the
  19500. * end of the existing archive. If the archive does not yet exists it
  19501. * is created.
  19502. * The $p_filelist parameter can be an array of string, each string
  19503. * representing a filename or a directory name with their path if
  19504. * needed. It can also be a single string with names separated by a
  19505. * single blank.
  19506. * The path indicated in $p_remove_dir will be removed from the
  19507. * memorized path of each file / directory listed when this path
  19508. * exists. By default nothing is removed (empty path '')
  19509. * The path indicated in $p_add_dir will be added at the beginning of
  19510. * the memorized path of each file / directory listed. However it can
  19511. * be set to empty ''. The adding of a path is done after the removing
  19512. * of path.
  19513. * The path add/remove ability enables the user to prepare an archive
  19514. * for extraction in a different path than the origin files are.
  19515. * If a file/dir is already in the archive it will only be added at the
  19516. * end of the archive. There is no update of the existing archived
  19517. * file/dir. However while extracting the archive, the last file will
  19518. * replace the first one. This results in a none optimization of the
  19519. * archive size.
  19520. * If a file/dir does not exist the file/dir is ignored. However an
  19521. * error text is send to PEAR error.
  19522. * If a file/dir is not readable the file/dir is ignored. However an
  19523. * error text is send to PEAR error.
  19524. *
  19525. * @param array $p_filelist An array of filenames and directory
  19526. * names, or a single string with names
  19527. * separated by a single blank space.
  19528. * @param string $p_add_dir A string which contains a path to be
  19529. * added to the memorized path of each
  19530. * element in the list.
  19531. * @param string $p_remove_dir A string which contains a path to be
  19532. * removed from the memorized path of
  19533. * each element in the list, when
  19534. * relevant.
  19535. *
  19536. * @return bool true on success, false on error.
  19537. */
  19538. public function addModify($p_filelist, $p_add_dir, $p_remove_dir = '')
  19539. {
  19540. $v_result = true;
  19541. if (!$this->_isArchive()) {
  19542. $v_result = $this->createModify(
  19543. $p_filelist,
  19544. $p_add_dir,
  19545. $p_remove_dir
  19546. );
  19547. } else {
  19548. if (is_array($p_filelist)) {
  19549. $v_list = $p_filelist;
  19550. } elseif (is_string($p_filelist)) {
  19551. $v_list = explode($this->_separator, $p_filelist);
  19552. } else {
  19553. $this->_error('Invalid file list');
  19554. return false;
  19555. }
  19556. $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
  19557. }
  19558. return $v_result;
  19559. }
  19560. /**
  19561. * This method add a single string as a file at the
  19562. * end of the existing archive. If the archive does not yet exists it
  19563. * is created.
  19564. *
  19565. * @param string $p_filename A string which contains the full
  19566. * filename path that will be associated
  19567. * with the string.
  19568. * @param string $p_string The content of the file added in
  19569. * the archive.
  19570. * @param bool|int $p_datetime A custom date/time (unix timestamp)
  19571. * for the file (optional).
  19572. * @param array $p_params An array of optional params:
  19573. * stamp => the datetime (replaces
  19574. * datetime above if it exists)
  19575. * mode => the permissions on the
  19576. * file (600 by default)
  19577. * type => is this a link? See the
  19578. * tar specification for details.
  19579. * (default = regular file)
  19580. * uid => the user ID of the file
  19581. * (default = 0 = root)
  19582. * gid => the group ID of the file
  19583. * (default = 0 = root)
  19584. *
  19585. * @return bool true on success, false on error.
  19586. */
  19587. public function addString($p_filename, $p_string, $p_datetime = false, $p_params = array())
  19588. {
  19589. $p_stamp = @$p_params["stamp"] ? $p_params["stamp"] : ($p_datetime ? $p_datetime : time());
  19590. $p_mode = @$p_params["mode"] ? $p_params["mode"] : 0600;
  19591. $p_type = @$p_params["type"] ? $p_params["type"] : "";
  19592. $p_uid = @$p_params["uid"] ? $p_params["uid"] : "";
  19593. $p_gid = @$p_params["gid"] ? $p_params["gid"] : "";
  19594. $v_result = true;
  19595. if (!$this->_isArchive()) {
  19596. if (!$this->_openWrite()) {
  19597. return false;
  19598. }
  19599. $this->_close();
  19600. }
  19601. if (!$this->_openAppend()) {
  19602. return false;
  19603. }
  19604. // Need to check the get back to the temporary file ? ....
  19605. $v_result = $this->_addString($p_filename, $p_string, $p_datetime, $p_params);
  19606. $this->_writeFooter();
  19607. $this->_close();
  19608. return $v_result;
  19609. }
  19610. /**
  19611. * This method extract all the content of the archive in the directory
  19612. * indicated by $p_path. When relevant the memorized path of the
  19613. * files/dir can be modified by removing the $p_remove_path path at the
  19614. * beginning of the file/dir path.
  19615. * While extracting a file, if the directory path does not exists it is
  19616. * created.
  19617. * While extracting a file, if the file already exists it is replaced
  19618. * without looking for last modification date.
  19619. * While extracting a file, if the file already exists and is write
  19620. * protected, the extraction is aborted.
  19621. * While extracting a file, if a directory with the same name already
  19622. * exists, the extraction is aborted.
  19623. * While extracting a directory, if a file with the same name already
  19624. * exists, the extraction is aborted.
  19625. * While extracting a file/directory if the destination directory exist
  19626. * and is write protected, or does not exist but can not be created,
  19627. * the extraction is aborted.
  19628. * If after extraction an extracted file does not show the correct
  19629. * stored file size, the extraction is aborted.
  19630. * When the extraction is aborted, a PEAR error text is set and false
  19631. * is returned. However the result can be a partial extraction that may
  19632. * need to be manually cleaned.
  19633. *
  19634. * @param string $p_path The path of the directory where the
  19635. * files/dir need to by extracted.
  19636. * @param string $p_remove_path Part of the memorized path that can be
  19637. * removed if present at the beginning of
  19638. * the file/dir path.
  19639. * @param boolean $p_preserve Preserve user/group ownership of files
  19640. * @param boolean $p_symlinks Allow symlinks.
  19641. *
  19642. * @return boolean true on success, false on error.
  19643. * @see extractList()
  19644. */
  19645. public function extractModify($p_path, $p_remove_path, $p_preserve = false, $p_symlinks = true)
  19646. {
  19647. $v_result = true;
  19648. $v_list_detail = array();
  19649. if ($v_result = $this->_openRead()) {
  19650. $v_result = $this->_extractList(
  19651. $p_path,
  19652. $v_list_detail,
  19653. "complete",
  19654. 0,
  19655. $p_remove_path,
  19656. $p_preserve,
  19657. $p_symlinks
  19658. );
  19659. $this->_close();
  19660. }
  19661. return $v_result;
  19662. }
  19663. /**
  19664. * This method extract from the archive one file identified by $p_filename.
  19665. * The return value is a string with the file content, or NULL on error.
  19666. *
  19667. * @param string $p_filename The path of the file to extract in a string.
  19668. *
  19669. * @return a string with the file content or NULL.
  19670. */
  19671. public function extractInString($p_filename)
  19672. {
  19673. if ($this->_openRead()) {
  19674. $v_result = $this->_extractInString($p_filename);
  19675. $this->_close();
  19676. } else {
  19677. $v_result = null;
  19678. }
  19679. return $v_result;
  19680. }
  19681. /**
  19682. * This method extract from the archive only the files indicated in the
  19683. * $p_filelist. These files are extracted in the current directory or
  19684. * in the directory indicated by the optional $p_path parameter.
  19685. * If indicated the $p_remove_path can be used in the same way as it is
  19686. * used in extractModify() method.
  19687. *
  19688. * @param array $p_filelist An array of filenames and directory names,
  19689. * or a single string with names separated
  19690. * by a single blank space.
  19691. * @param string $p_path The path of the directory where the
  19692. * files/dir need to by extracted.
  19693. * @param string $p_remove_path Part of the memorized path that can be
  19694. * removed if present at the beginning of
  19695. * the file/dir path.
  19696. * @param boolean $p_preserve Preserve user/group ownership of files
  19697. * @param boolean $p_symlinks Allow symlinks.
  19698. *
  19699. * @return bool true on success, false on error.
  19700. * @see extractModify()
  19701. */
  19702. public function extractList($p_filelist, $p_path = '', $p_remove_path = '', $p_preserve = false, $p_symlinks = true)
  19703. {
  19704. $v_result = true;
  19705. $v_list_detail = array();
  19706. if (is_array($p_filelist)) {
  19707. $v_list = $p_filelist;
  19708. } elseif (is_string($p_filelist)) {
  19709. $v_list = explode($this->_separator, $p_filelist);
  19710. } else {
  19711. $this->_error('Invalid string list');
  19712. return false;
  19713. }
  19714. if ($v_result = $this->_openRead()) {
  19715. $v_result = $this->_extractList(
  19716. $p_path,
  19717. $v_list_detail,
  19718. "partial",
  19719. $v_list,
  19720. $p_remove_path,
  19721. $p_preserve,
  19722. $p_symlinks
  19723. );
  19724. $this->_close();
  19725. }
  19726. return $v_result;
  19727. }
  19728. /**
  19729. * This method set specific attributes of the archive. It uses a variable
  19730. * list of parameters, in the format attribute code + attribute values :
  19731. * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ',');
  19732. *
  19733. * @return bool true on success, false on error.
  19734. */
  19735. public function setAttribute()
  19736. {
  19737. $v_result = true;
  19738. // ----- Get the number of variable list of arguments
  19739. if (($v_size = func_num_args()) == 0) {
  19740. return true;
  19741. }
  19742. // ----- Get the arguments
  19743. $v_att_list = func_get_args();
  19744. // ----- Read the attributes
  19745. $i = 0;
  19746. while ($i < $v_size) {
  19747. // ----- Look for next option
  19748. switch ($v_att_list[$i]) {
  19749. // ----- Look for options that request a string value
  19750. case ARCHIVE_TAR_ATT_SEPARATOR :
  19751. // ----- Check the number of parameters
  19752. if (($i + 1) >= $v_size) {
  19753. $this->_error(
  19754. 'Invalid number of parameters for '
  19755. . 'attribute ARCHIVE_TAR_ATT_SEPARATOR'
  19756. );
  19757. return false;
  19758. }
  19759. // ----- Get the value
  19760. $this->_separator = $v_att_list[$i + 1];
  19761. $i++;
  19762. break;
  19763. default :
  19764. $this->_error('Unknown attribute code ' . $v_att_list[$i] . '');
  19765. return false;
  19766. }
  19767. // ----- Next attribute
  19768. $i++;
  19769. }
  19770. return $v_result;
  19771. }
  19772. /**
  19773. * This method sets the regular expression for ignoring files and directories
  19774. * at import, for example:
  19775. * $arch->setIgnoreRegexp("#CVS|\.svn#");
  19776. *
  19777. * @param string $regexp regular expression defining which files or directories to ignore
  19778. */
  19779. public function setIgnoreRegexp($regexp)
  19780. {
  19781. $this->_ignore_regexp = $regexp;
  19782. }
  19783. /**
  19784. * This method sets the regular expression for ignoring all files and directories
  19785. * matching the filenames in the array list at import, for example:
  19786. * $arch->setIgnoreList(array('CVS', '.svn', 'bin/tool'));
  19787. *
  19788. * @param array $list a list of file or directory names to ignore
  19789. *
  19790. * @access public
  19791. */
  19792. public function setIgnoreList($list)
  19793. {
  19794. $list = str_replace(array('#', '.', '^', '$'), array('\#', '\.', '\^', '\$'), $list);
  19795. $regexp = '#/' . join('$|/', $list) . '#';
  19796. $this->setIgnoreRegexp($regexp);
  19797. }
  19798. /**
  19799. * @param string $p_message
  19800. */
  19801. public function _error($p_message)
  19802. {
  19803. $this->error_object = $this->raiseError($p_message);
  19804. }
  19805. /**
  19806. * @param string $p_message
  19807. */
  19808. public function _warning($p_message)
  19809. {
  19810. $this->error_object = $this->raiseError($p_message);
  19811. }
  19812. /**
  19813. * @param string $p_filename
  19814. * @return bool
  19815. */
  19816. public function _isArchive($p_filename = null)
  19817. {
  19818. if ($p_filename == null) {
  19819. $p_filename = $this->_tarname;
  19820. }
  19821. clearstatcache();
  19822. return @is_file($p_filename) && !@is_link($p_filename);
  19823. }
  19824. /**
  19825. * @return bool
  19826. */
  19827. public function _openWrite()
  19828. {
  19829. if ($this->_compress_type == 'gz' && function_exists('gzopen')) {
  19830. $this->_file = @gzopen($this->_tarname, "wb9");
  19831. } else {
  19832. if ($this->_compress_type == 'bz2' && function_exists('bzopen')) {
  19833. $this->_file = @bzopen($this->_tarname, "w");
  19834. } else {
  19835. if ($this->_compress_type == 'lzma2' && function_exists('xzopen')) {
  19836. $this->_file = @xzopen($this->_tarname, 'w');
  19837. } else {
  19838. if ($this->_compress_type == 'none') {
  19839. $this->_file = @fopen($this->_tarname, "wb");
  19840. } else {
  19841. $this->_error(
  19842. 'Unknown or missing compression type ('
  19843. . $this->_compress_type . ')'
  19844. );
  19845. return false;
  19846. }
  19847. }
  19848. }
  19849. }
  19850. if ($this->_file == 0) {
  19851. $this->_error(
  19852. 'Unable to open in write mode \''
  19853. . $this->_tarname . '\''
  19854. );
  19855. return false;
  19856. }
  19857. return true;
  19858. }
  19859. /**
  19860. * @return bool
  19861. */
  19862. public function _openRead()
  19863. {
  19864. if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
  19865. // ----- Look if a local copy need to be done
  19866. if ($this->_temp_tarname == '') {
  19867. $this->_temp_tarname = uniqid('tar') . '.tmp';
  19868. if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
  19869. $this->_error(
  19870. 'Unable to open in read mode \''
  19871. . $this->_tarname . '\''
  19872. );
  19873. $this->_temp_tarname = '';
  19874. return false;
  19875. }
  19876. if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
  19877. $this->_error(
  19878. 'Unable to open in write mode \''
  19879. . $this->_temp_tarname . '\''
  19880. );
  19881. $this->_temp_tarname = '';
  19882. return false;
  19883. }
  19884. while ($v_data = @fread($v_file_from, 1024)) {
  19885. @fwrite($v_file_to, $v_data);
  19886. }
  19887. @fclose($v_file_from);
  19888. @fclose($v_file_to);
  19889. }
  19890. // ----- File to open if the local copy
  19891. $v_filename = $this->_temp_tarname;
  19892. } else {
  19893. // ----- File to open if the normal Tar file
  19894. $v_filename = $this->_tarname;
  19895. }
  19896. if ($this->_compress_type == 'gz' && function_exists('gzopen')) {
  19897. $this->_file = @gzopen($v_filename, "rb");
  19898. } else {
  19899. if ($this->_compress_type == 'bz2' && function_exists('bzopen')) {
  19900. $this->_file = @bzopen($v_filename, "r");
  19901. } else {
  19902. if ($this->_compress_type == 'lzma2' && function_exists('xzopen')) {
  19903. $this->_file = @xzopen($v_filename, "r");
  19904. } else {
  19905. if ($this->_compress_type == 'none') {
  19906. $this->_file = @fopen($v_filename, "rb");
  19907. } else {
  19908. $this->_error(
  19909. 'Unknown or missing compression type ('
  19910. . $this->_compress_type . ')'
  19911. );
  19912. return false;
  19913. }
  19914. }
  19915. }
  19916. }
  19917. if ($this->_file == 0) {
  19918. $this->_error('Unable to open in read mode \'' . $v_filename . '\'');
  19919. return false;
  19920. }
  19921. return true;
  19922. }
  19923. /**
  19924. * @return bool
  19925. */
  19926. public function _openReadWrite()
  19927. {
  19928. if ($this->_compress_type == 'gz') {
  19929. $this->_file = @gzopen($this->_tarname, "r+b");
  19930. } else {
  19931. if ($this->_compress_type == 'bz2') {
  19932. $this->_error(
  19933. 'Unable to open bz2 in read/write mode \''
  19934. . $this->_tarname . '\' (limitation of bz2 extension)'
  19935. );
  19936. return false;
  19937. } else {
  19938. if ($this->_compress_type == 'lzma2') {
  19939. $this->_error(
  19940. 'Unable to open lzma2 in read/write mode \''
  19941. . $this->_tarname . '\' (limitation of lzma2 extension)'
  19942. );
  19943. return false;
  19944. } else {
  19945. if ($this->_compress_type == 'none') {
  19946. $this->_file = @fopen($this->_tarname, "r+b");
  19947. } else {
  19948. $this->_error(
  19949. 'Unknown or missing compression type ('
  19950. . $this->_compress_type . ')'
  19951. );
  19952. return false;
  19953. }
  19954. }
  19955. }
  19956. }
  19957. if ($this->_file == 0) {
  19958. $this->_error(
  19959. 'Unable to open in read/write mode \''
  19960. . $this->_tarname . '\''
  19961. );
  19962. return false;
  19963. }
  19964. return true;
  19965. }
  19966. /**
  19967. * @return bool
  19968. */
  19969. public function _close()
  19970. {
  19971. //if (isset($this->_file)) {
  19972. if (is_resource($this->_file)) {
  19973. if ($this->_compress_type == 'gz') {
  19974. @gzclose($this->_file);
  19975. } else {
  19976. if ($this->_compress_type == 'bz2') {
  19977. @bzclose($this->_file);
  19978. } else {
  19979. if ($this->_compress_type == 'lzma2') {
  19980. @xzclose($this->_file);
  19981. } else {
  19982. if ($this->_compress_type == 'none') {
  19983. @fclose($this->_file);
  19984. } else {
  19985. $this->_error(
  19986. 'Unknown or missing compression type ('
  19987. . $this->_compress_type . ')'
  19988. );
  19989. }
  19990. }
  19991. }
  19992. }
  19993. $this->_file = 0;
  19994. }
  19995. // ----- Look if a local copy need to be erase
  19996. // Note that it might be interesting to keep the url for a time : ToDo
  19997. if ($this->_temp_tarname != '') {
  19998. @unlink($this->_temp_tarname);
  19999. $this->_temp_tarname = '';
  20000. }
  20001. return true;
  20002. }
  20003. /**
  20004. * @return bool
  20005. */
  20006. public function _cleanFile()
  20007. {
  20008. $this->_close();
  20009. // ----- Look for a local copy
  20010. if ($this->_temp_tarname != '') {
  20011. // ----- Remove the local copy but not the remote tarname
  20012. @unlink($this->_temp_tarname);
  20013. $this->_temp_tarname = '';
  20014. } else {
  20015. // ----- Remove the local tarname file
  20016. @unlink($this->_tarname);
  20017. }
  20018. $this->_tarname = '';
  20019. return true;
  20020. }
  20021. /**
  20022. * @param mixed $p_binary_data
  20023. * @param integer $p_len
  20024. * @return bool
  20025. */
  20026. public function _writeBlock($p_binary_data, $p_len = null)
  20027. {
  20028. if (is_resource($this->_file)) {
  20029. if ($p_len === null) {
  20030. if ($this->_compress_type == 'gz') {
  20031. @gzputs($this->_file, $p_binary_data);
  20032. } else {
  20033. if ($this->_compress_type == 'bz2') {
  20034. @bzwrite($this->_file, $p_binary_data);
  20035. } else {
  20036. if ($this->_compress_type == 'lzma2') {
  20037. @xzwrite($this->_file, $p_binary_data);
  20038. } else {
  20039. if ($this->_compress_type == 'none') {
  20040. @fputs($this->_file, $p_binary_data);
  20041. } else {
  20042. $this->_error(
  20043. 'Unknown or missing compression type ('
  20044. . $this->_compress_type . ')'
  20045. );
  20046. }
  20047. }
  20048. }
  20049. }
  20050. } else {
  20051. if ($this->_compress_type == 'gz') {
  20052. @gzputs($this->_file, $p_binary_data, $p_len);
  20053. } else {
  20054. if ($this->_compress_type == 'bz2') {
  20055. @bzwrite($this->_file, $p_binary_data, $p_len);
  20056. } else {
  20057. if ($this->_compress_type == 'lzma2') {
  20058. @xzwrite($this->_file, $p_binary_data, $p_len);
  20059. } else {
  20060. if ($this->_compress_type == 'none') {
  20061. @fputs($this->_file, $p_binary_data, $p_len);
  20062. } else {
  20063. $this->_error(
  20064. 'Unknown or missing compression type ('
  20065. . $this->_compress_type . ')'
  20066. );
  20067. }
  20068. }
  20069. }
  20070. }
  20071. }
  20072. }
  20073. return true;
  20074. }
  20075. /**
  20076. * @return null|string
  20077. */
  20078. public function _readBlock()
  20079. {
  20080. $v_block = null;
  20081. if (is_resource($this->_file)) {
  20082. if ($this->_compress_type == 'gz') {
  20083. $v_block = @gzread($this->_file, 512);
  20084. } else {
  20085. if ($this->_compress_type == 'bz2') {
  20086. $v_block = @bzread($this->_file, 512);
  20087. } else {
  20088. if ($this->_compress_type == 'lzma2') {
  20089. $v_block = @xzread($this->_file, 512);
  20090. } else {
  20091. if ($this->_compress_type == 'none') {
  20092. $v_block = @fread($this->_file, 512);
  20093. } else {
  20094. $this->_error(
  20095. 'Unknown or missing compression type ('
  20096. . $this->_compress_type . ')'
  20097. );
  20098. }
  20099. }
  20100. }
  20101. }
  20102. }
  20103. return $v_block;
  20104. }
  20105. /**
  20106. * @param null $p_len
  20107. * @return bool
  20108. */
  20109. public function _jumpBlock($p_len = null)
  20110. {
  20111. if (is_resource($this->_file)) {
  20112. if ($p_len === null) {
  20113. $p_len = 1;
  20114. }
  20115. if ($this->_compress_type == 'gz') {
  20116. @gzseek($this->_file, gztell($this->_file) + ($p_len * 512));
  20117. } else {
  20118. if ($this->_compress_type == 'bz2') {
  20119. // ----- Replace missing bztell() and bzseek()
  20120. for ($i = 0; $i < $p_len; $i++) {
  20121. $this->_readBlock();
  20122. }
  20123. } else {
  20124. if ($this->_compress_type == 'lzma2') {
  20125. // ----- Replace missing xztell() and xzseek()
  20126. for ($i = 0; $i < $p_len; $i++) {
  20127. $this->_readBlock();
  20128. }
  20129. } else {
  20130. if ($this->_compress_type == 'none') {
  20131. @fseek($this->_file, $p_len * 512, SEEK_CUR);
  20132. } else {
  20133. $this->_error(
  20134. 'Unknown or missing compression type ('
  20135. . $this->_compress_type . ')'
  20136. );
  20137. }
  20138. }
  20139. }
  20140. }
  20141. }
  20142. return true;
  20143. }
  20144. /**
  20145. * @return bool
  20146. */
  20147. public function _writeFooter()
  20148. {
  20149. if (is_resource($this->_file)) {
  20150. // ----- Write the last 0 filled block for end of archive
  20151. $v_binary_data = pack('a1024', '');
  20152. $this->_writeBlock($v_binary_data);
  20153. }
  20154. return true;
  20155. }
  20156. /**
  20157. * @param array $p_list
  20158. * @param string $p_add_dir
  20159. * @param string $p_remove_dir
  20160. * @return bool
  20161. */
  20162. public function _addList($p_list, $p_add_dir, $p_remove_dir)
  20163. {
  20164. $v_result = true;
  20165. $v_header = array();
  20166. // ----- Remove potential windows directory separator
  20167. $p_add_dir = $this->_translateWinPath($p_add_dir);
  20168. $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
  20169. if (!$this->_file) {
  20170. $this->_error('Invalid file descriptor');
  20171. return false;
  20172. }
  20173. if (sizeof($p_list) == 0) {
  20174. return true;
  20175. }
  20176. foreach ($p_list as $v_filename) {
  20177. if (!$v_result) {
  20178. break;
  20179. }
  20180. // ----- Skip the current tar name
  20181. if ($v_filename == $this->_tarname) {
  20182. continue;
  20183. }
  20184. if ($v_filename == '') {
  20185. continue;
  20186. }
  20187. // ----- ignore files and directories matching the ignore regular expression
  20188. if ($this->_ignore_regexp && preg_match($this->_ignore_regexp, '/' . $v_filename)) {
  20189. $this->_warning("File '$v_filename' ignored");
  20190. continue;
  20191. }
  20192. if (!file_exists($v_filename) && !is_link($v_filename)) {
  20193. $this->_warning("File '$v_filename' does not exist");
  20194. continue;
  20195. }
  20196. // ----- Add the file or directory header
  20197. if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir)) {
  20198. return false;
  20199. }
  20200. if (@is_dir($v_filename) && !@is_link($v_filename)) {
  20201. if (!($p_hdir = opendir($v_filename))) {
  20202. $this->_warning("Directory '$v_filename' can not be read");
  20203. continue;
  20204. }
  20205. while (false !== ($p_hitem = readdir($p_hdir))) {
  20206. if (($p_hitem != '.') && ($p_hitem != '..')) {
  20207. if ($v_filename != ".") {
  20208. $p_temp_list[0] = $v_filename . '/' . $p_hitem;
  20209. } else {
  20210. $p_temp_list[0] = $p_hitem;
  20211. }
  20212. $v_result = $this->_addList(
  20213. $p_temp_list,
  20214. $p_add_dir,
  20215. $p_remove_dir
  20216. );
  20217. }
  20218. }
  20219. unset($p_temp_list);
  20220. unset($p_hdir);
  20221. unset($p_hitem);
  20222. }
  20223. }
  20224. return $v_result;
  20225. }
  20226. /**
  20227. * @param string $p_filename
  20228. * @param mixed $p_header
  20229. * @param string $p_add_dir
  20230. * @param string $p_remove_dir
  20231. * @param null $v_stored_filename
  20232. * @return bool
  20233. */
  20234. public function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir, $v_stored_filename = null)
  20235. {
  20236. if (!$this->_file) {
  20237. $this->_error('Invalid file descriptor');
  20238. return false;
  20239. }
  20240. if ($p_filename == '') {
  20241. $this->_error('Invalid file name');
  20242. return false;
  20243. }
  20244. if (is_null($v_stored_filename)) {
  20245. // ----- Calculate the stored filename
  20246. $p_filename = $this->_translateWinPath($p_filename, false);
  20247. $v_stored_filename = $p_filename;
  20248. if (strcmp($p_filename, $p_remove_dir) == 0) {
  20249. return true;
  20250. }
  20251. if ($p_remove_dir != '') {
  20252. if (substr($p_remove_dir, -1) != '/') {
  20253. $p_remove_dir .= '/';
  20254. }
  20255. if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir) {
  20256. $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
  20257. }
  20258. }
  20259. $v_stored_filename = $this->_translateWinPath($v_stored_filename);
  20260. if ($p_add_dir != '') {
  20261. if (substr($p_add_dir, -1) == '/') {
  20262. $v_stored_filename = $p_add_dir . $v_stored_filename;
  20263. } else {
  20264. $v_stored_filename = $p_add_dir . '/' . $v_stored_filename;
  20265. }
  20266. }
  20267. $v_stored_filename = $this->_pathReduction($v_stored_filename);
  20268. }
  20269. if ($this->_isArchive($p_filename)) {
  20270. if (($v_file = @fopen($p_filename, "rb")) == 0) {
  20271. $this->_warning(
  20272. "Unable to open file '" . $p_filename
  20273. . "' in binary read mode"
  20274. );
  20275. return true;
  20276. }
  20277. if (!$this->_writeHeader($p_filename, $v_stored_filename)) {
  20278. return false;
  20279. }
  20280. while (($v_buffer = fread($v_file, $this->buffer_length)) != '') {
  20281. $buffer_length = strlen("$v_buffer");
  20282. if ($buffer_length != $this->buffer_length) {
  20283. $pack_size = ((int)($buffer_length / 512) + ($buffer_length % 512 !== 0 ? 1 : 0)) * 512;
  20284. $pack_format = sprintf('a%d', $pack_size);
  20285. } else {
  20286. $pack_format = sprintf('a%d', $this->buffer_length);
  20287. }
  20288. $v_binary_data = pack($pack_format, "$v_buffer");
  20289. $this->_writeBlock($v_binary_data);
  20290. }
  20291. fclose($v_file);
  20292. } else {
  20293. // ----- Only header for dir
  20294. if (!$this->_writeHeader($p_filename, $v_stored_filename)) {
  20295. return false;
  20296. }
  20297. }
  20298. return true;
  20299. }
  20300. /**
  20301. * @param string $p_filename
  20302. * @param string $p_string
  20303. * @param bool $p_datetime
  20304. * @param array $p_params
  20305. * @return bool
  20306. */
  20307. public function _addString($p_filename, $p_string, $p_datetime = false, $p_params = array())
  20308. {
  20309. $p_stamp = @$p_params["stamp"] ? $p_params["stamp"] : ($p_datetime ? $p_datetime : time());
  20310. $p_mode = @$p_params["mode"] ? $p_params["mode"] : 0600;
  20311. $p_type = @$p_params["type"] ? $p_params["type"] : "";
  20312. $p_uid = @$p_params["uid"] ? $p_params["uid"] : 0;
  20313. $p_gid = @$p_params["gid"] ? $p_params["gid"] : 0;
  20314. if (!$this->_file) {
  20315. $this->_error('Invalid file descriptor');
  20316. return false;
  20317. }
  20318. if ($p_filename == '') {
  20319. $this->_error('Invalid file name');
  20320. return false;
  20321. }
  20322. // ----- Calculate the stored filename
  20323. $p_filename = $this->_translateWinPath($p_filename, false);
  20324. // ----- If datetime is not specified, set current time
  20325. if ($p_datetime === false) {
  20326. $p_datetime = time();
  20327. }
  20328. if (!$this->_writeHeaderBlock(
  20329. $p_filename,
  20330. strlen($p_string),
  20331. $p_stamp,
  20332. $p_mode,
  20333. $p_type,
  20334. $p_uid,
  20335. $p_gid
  20336. )
  20337. ) {
  20338. return false;
  20339. }
  20340. $i = 0;
  20341. while (($v_buffer = substr($p_string, (($i++) * 512), 512)) != '') {
  20342. $v_binary_data = pack("a512", $v_buffer);
  20343. $this->_writeBlock($v_binary_data);
  20344. }
  20345. return true;
  20346. }
  20347. /**
  20348. * @param string $p_filename
  20349. * @param string $p_stored_filename
  20350. * @return bool
  20351. */
  20352. public function _writeHeader($p_filename, $p_stored_filename)
  20353. {
  20354. if ($p_stored_filename == '') {
  20355. $p_stored_filename = $p_filename;
  20356. }
  20357. $v_reduced_filename = $this->_pathReduction($p_stored_filename);
  20358. if (strlen($v_reduced_filename) > 99) {
  20359. if (!$this->_writeLongHeader($v_reduced_filename, false)) {
  20360. return false;
  20361. }
  20362. }
  20363. $v_linkname = '';
  20364. if (@is_link($p_filename)) {
  20365. $v_linkname = readlink($p_filename);
  20366. }
  20367. if (strlen($v_linkname) > 99) {
  20368. if (!$this->_writeLongHeader($v_linkname, true)) {
  20369. return false;
  20370. }
  20371. }
  20372. $v_info = lstat($p_filename);
  20373. $v_uid = sprintf("%07s", DecOct($v_info[4]));
  20374. $v_gid = sprintf("%07s", DecOct($v_info[5]));
  20375. $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777));
  20376. $v_mtime = sprintf("%011s", DecOct($v_info['mtime']));
  20377. if (@is_link($p_filename)) {
  20378. $v_typeflag = '2';
  20379. $v_size = sprintf("%011s", DecOct(0));
  20380. } elseif (@is_dir($p_filename)) {
  20381. $v_typeflag = "5";
  20382. $v_size = sprintf("%011s", DecOct(0));
  20383. } else {
  20384. $v_typeflag = '0';
  20385. clearstatcache();
  20386. $v_size = sprintf("%011s", DecOct($v_info['size']));
  20387. }
  20388. $v_magic = 'ustar ';
  20389. $v_version = ' ';
  20390. $v_uname = '';
  20391. $v_gname = '';
  20392. if (function_exists('posix_getpwuid')) {
  20393. $userinfo = posix_getpwuid($v_info[4]);
  20394. $groupinfo = posix_getgrgid($v_info[5]);
  20395. if (isset($userinfo['name'])) {
  20396. $v_uname = $userinfo['name'];
  20397. }
  20398. if (isset($groupinfo['name'])) {
  20399. $v_gname = $groupinfo['name'];
  20400. }
  20401. }
  20402. $v_devmajor = '';
  20403. $v_devminor = '';
  20404. $v_prefix = '';
  20405. $v_binary_data_first = pack(
  20406. "a100a8a8a8a12a12",
  20407. $v_reduced_filename,
  20408. $v_perms,
  20409. $v_uid,
  20410. $v_gid,
  20411. $v_size,
  20412. $v_mtime
  20413. );
  20414. $v_binary_data_last = pack(
  20415. "a1a100a6a2a32a32a8a8a155a12",
  20416. $v_typeflag,
  20417. $v_linkname,
  20418. $v_magic,
  20419. $v_version,
  20420. $v_uname,
  20421. $v_gname,
  20422. $v_devmajor,
  20423. $v_devminor,
  20424. $v_prefix,
  20425. ''
  20426. );
  20427. // ----- Calculate the checksum
  20428. $v_checksum = 0;
  20429. // ..... First part of the header
  20430. for ($i = 0; $i < 148; $i++) {
  20431. $v_checksum += ord(substr($v_binary_data_first, $i, 1));
  20432. }
  20433. // ..... Ignore the checksum value and replace it by ' ' (space)
  20434. for ($i = 148; $i < 156; $i++) {
  20435. $v_checksum += ord(' ');
  20436. }
  20437. // ..... Last part of the header
  20438. for ($i = 156, $j = 0; $i < 512; $i++, $j++) {
  20439. $v_checksum += ord(substr($v_binary_data_last, $j, 1));
  20440. }
  20441. // ----- Write the first 148 bytes of the header in the archive
  20442. $this->_writeBlock($v_binary_data_first, 148);
  20443. // ----- Write the calculated checksum
  20444. $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum));
  20445. $v_binary_data = pack("a8", $v_checksum);
  20446. $this->_writeBlock($v_binary_data, 8);
  20447. // ----- Write the last 356 bytes of the header in the archive
  20448. $this->_writeBlock($v_binary_data_last, 356);
  20449. return true;
  20450. }
  20451. /**
  20452. * @param string $p_filename
  20453. * @param int $p_size
  20454. * @param int $p_mtime
  20455. * @param int $p_perms
  20456. * @param string $p_type
  20457. * @param int $p_uid
  20458. * @param int $p_gid
  20459. * @return bool
  20460. */
  20461. public function _writeHeaderBlock(
  20462. $p_filename,
  20463. $p_size,
  20464. $p_mtime = 0,
  20465. $p_perms = 0,
  20466. $p_type = '',
  20467. $p_uid = 0,
  20468. $p_gid = 0
  20469. )
  20470. {
  20471. $p_filename = $this->_pathReduction($p_filename);
  20472. if (strlen($p_filename) > 99) {
  20473. if (!$this->_writeLongHeader($p_filename, false)) {
  20474. return false;
  20475. }
  20476. }
  20477. if ($p_type == "5") {
  20478. $v_size = sprintf("%011s", DecOct(0));
  20479. } else {
  20480. $v_size = sprintf("%011s", DecOct($p_size));
  20481. }
  20482. $v_uid = sprintf("%07s", DecOct($p_uid));
  20483. $v_gid = sprintf("%07s", DecOct($p_gid));
  20484. $v_perms = sprintf("%07s", DecOct($p_perms & 000777));
  20485. $v_mtime = sprintf("%11s", DecOct($p_mtime));
  20486. $v_linkname = '';
  20487. $v_magic = 'ustar ';
  20488. $v_version = ' ';
  20489. if (function_exists('posix_getpwuid')) {
  20490. $userinfo = posix_getpwuid($p_uid);
  20491. $groupinfo = posix_getgrgid($p_gid);
  20492. if ($userinfo === false || $groupinfo === false) {
  20493. $v_uname = '';
  20494. $v_gname = '';
  20495. } else {
  20496. $v_uname = $userinfo['name'];
  20497. $v_gname = $groupinfo['name'];
  20498. }
  20499. } else {
  20500. $v_uname = '';
  20501. $v_gname = '';
  20502. }
  20503. $v_devmajor = '';
  20504. $v_devminor = '';
  20505. $v_prefix = '';
  20506. $v_binary_data_first = pack(
  20507. "a100a8a8a8a12A12",
  20508. $p_filename,
  20509. $v_perms,
  20510. $v_uid,
  20511. $v_gid,
  20512. $v_size,
  20513. $v_mtime
  20514. );
  20515. $v_binary_data_last = pack(
  20516. "a1a100a6a2a32a32a8a8a155a12",
  20517. $p_type,
  20518. $v_linkname,
  20519. $v_magic,
  20520. $v_version,
  20521. $v_uname,
  20522. $v_gname,
  20523. $v_devmajor,
  20524. $v_devminor,
  20525. $v_prefix,
  20526. ''
  20527. );
  20528. // ----- Calculate the checksum
  20529. $v_checksum = 0;
  20530. // ..... First part of the header
  20531. for ($i = 0; $i < 148; $i++) {
  20532. $v_checksum += ord(substr($v_binary_data_first, $i, 1));
  20533. }
  20534. // ..... Ignore the checksum value and replace it by ' ' (space)
  20535. for ($i = 148; $i < 156; $i++) {
  20536. $v_checksum += ord(' ');
  20537. }
  20538. // ..... Last part of the header
  20539. for ($i = 156, $j = 0; $i < 512; $i++, $j++) {
  20540. $v_checksum += ord(substr($v_binary_data_last, $j, 1));
  20541. }
  20542. // ----- Write the first 148 bytes of the header in the archive
  20543. $this->_writeBlock($v_binary_data_first, 148);
  20544. // ----- Write the calculated checksum
  20545. $v_checksum = sprintf("%06s ", DecOct($v_checksum));
  20546. $v_binary_data = pack("a8", $v_checksum);
  20547. $this->_writeBlock($v_binary_data, 8);
  20548. // ----- Write the last 356 bytes of the header in the archive
  20549. $this->_writeBlock($v_binary_data_last, 356);
  20550. return true;
  20551. }
  20552. /**
  20553. * @param string $p_filename
  20554. * @return bool
  20555. */
  20556. public function _writeLongHeader($p_filename, $is_link = false)
  20557. {
  20558. $v_uid = sprintf("%07s", 0);
  20559. $v_gid = sprintf("%07s", 0);
  20560. $v_perms = sprintf("%07s", 0);
  20561. $v_size = sprintf("%'011s", DecOct(strlen($p_filename)));
  20562. $v_mtime = sprintf("%011s", 0);
  20563. $v_typeflag = ($is_link ? 'K' : 'L');
  20564. $v_linkname = '';
  20565. $v_magic = 'ustar ';
  20566. $v_version = ' ';
  20567. $v_uname = '';
  20568. $v_gname = '';
  20569. $v_devmajor = '';
  20570. $v_devminor = '';
  20571. $v_prefix = '';
  20572. $v_binary_data_first = pack(
  20573. "a100a8a8a8a12a12",
  20574. '././@LongLink',
  20575. $v_perms,
  20576. $v_uid,
  20577. $v_gid,
  20578. $v_size,
  20579. $v_mtime
  20580. );
  20581. $v_binary_data_last = pack(
  20582. "a1a100a6a2a32a32a8a8a155a12",
  20583. $v_typeflag,
  20584. $v_linkname,
  20585. $v_magic,
  20586. $v_version,
  20587. $v_uname,
  20588. $v_gname,
  20589. $v_devmajor,
  20590. $v_devminor,
  20591. $v_prefix,
  20592. ''
  20593. );
  20594. // ----- Calculate the checksum
  20595. $v_checksum = 0;
  20596. // ..... First part of the header
  20597. for ($i = 0; $i < 148; $i++) {
  20598. $v_checksum += ord(substr($v_binary_data_first, $i, 1));
  20599. }
  20600. // ..... Ignore the checksum value and replace it by ' ' (space)
  20601. for ($i = 148; $i < 156; $i++) {
  20602. $v_checksum += ord(' ');
  20603. }
  20604. // ..... Last part of the header
  20605. for ($i = 156, $j = 0; $i < 512; $i++, $j++) {
  20606. $v_checksum += ord(substr($v_binary_data_last, $j, 1));
  20607. }
  20608. // ----- Write the first 148 bytes of the header in the archive
  20609. $this->_writeBlock($v_binary_data_first, 148);
  20610. // ----- Write the calculated checksum
  20611. $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum));
  20612. $v_binary_data = pack("a8", $v_checksum);
  20613. $this->_writeBlock($v_binary_data, 8);
  20614. // ----- Write the last 356 bytes of the header in the archive
  20615. $this->_writeBlock($v_binary_data_last, 356);
  20616. // ----- Write the filename as content of the block
  20617. $i = 0;
  20618. while (($v_buffer = substr($p_filename, (($i++) * 512), 512)) != '') {
  20619. $v_binary_data = pack("a512", "$v_buffer");
  20620. $this->_writeBlock($v_binary_data);
  20621. }
  20622. return true;
  20623. }
  20624. /**
  20625. * @param mixed $v_binary_data
  20626. * @param mixed $v_header
  20627. * @return bool
  20628. */
  20629. public function _readHeader($v_binary_data, &$v_header)
  20630. {
  20631. if (strlen($v_binary_data) == 0) {
  20632. $v_header['filename'] = '';
  20633. return true;
  20634. }
  20635. if (strlen($v_binary_data) != 512) {
  20636. $v_header['filename'] = '';
  20637. $this->_error('Invalid block size : ' . strlen($v_binary_data));
  20638. return false;
  20639. }
  20640. if (!is_array($v_header)) {
  20641. $v_header = array();
  20642. }
  20643. // ----- Calculate the checksum
  20644. $v_checksum = 0;
  20645. // ..... First part of the header
  20646. $v_binary_split = str_split($v_binary_data);
  20647. $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 0, 148)));
  20648. $v_checksum += array_sum(array_map('ord', array(' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',)));
  20649. $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 156, 512)));
  20650. $v_data = unpack($this->_fmt, $v_binary_data);
  20651. if (strlen($v_data["prefix"]) > 0) {
  20652. $v_data["filename"] = "$v_data[prefix]/$v_data[filename]";
  20653. }
  20654. // ----- Extract the checksum
  20655. $v_data_checksum = trim($v_data['checksum']);
  20656. if (!preg_match('/^[0-7]*$/', $v_data_checksum)) {
  20657. $this->_error(
  20658. 'Invalid checksum for file "' . $v_data['filename']
  20659. . '" : ' . $v_data_checksum . ' extracted'
  20660. );
  20661. return false;
  20662. }
  20663. $v_header['checksum'] = OctDec($v_data_checksum);
  20664. if ($v_header['checksum'] != $v_checksum) {
  20665. $v_header['filename'] = '';
  20666. // ----- Look for last block (empty block)
  20667. if (($v_checksum == 256) && ($v_header['checksum'] == 0)) {
  20668. return true;
  20669. }
  20670. $this->_error(
  20671. 'Invalid checksum for file "' . $v_data['filename']
  20672. . '" : ' . $v_checksum . ' calculated, '
  20673. . $v_header['checksum'] . ' expected'
  20674. );
  20675. return false;
  20676. }
  20677. // ----- Extract the properties
  20678. $v_header['filename'] = rtrim($v_data['filename'], "\0");
  20679. if ($this->_isMaliciousFilename($v_header['filename'])) {
  20680. $this->_error(
  20681. 'Malicious .tar detected, file "' . $v_header['filename'] .
  20682. '" will not install in desired directory tree'
  20683. );
  20684. return false;
  20685. }
  20686. $v_header['mode'] = OctDec(trim($v_data['mode']));
  20687. $v_header['uid'] = OctDec(trim($v_data['uid']));
  20688. $v_header['gid'] = OctDec(trim($v_data['gid']));
  20689. $v_header['size'] = $this->_tarRecToSize($v_data['size']);
  20690. $v_header['mtime'] = OctDec(trim($v_data['mtime']));
  20691. if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
  20692. $v_header['size'] = 0;
  20693. }
  20694. $v_header['link'] = trim($v_data['link']);
  20695. /* ----- All these fields are removed form the header because
  20696. they do not carry interesting info
  20697. $v_header[magic] = trim($v_data[magic]);
  20698. $v_header[version] = trim($v_data[version]);
  20699. $v_header[uname] = trim($v_data[uname]);
  20700. $v_header[gname] = trim($v_data[gname]);
  20701. $v_header[devmajor] = trim($v_data[devmajor]);
  20702. $v_header[devminor] = trim($v_data[devminor]);
  20703. */
  20704. return true;
  20705. }
  20706. /**
  20707. * Convert Tar record size to actual size
  20708. *
  20709. * @param string $tar_size
  20710. * @return size of tar record in bytes
  20711. */
  20712. private function _tarRecToSize($tar_size)
  20713. {
  20714. /*
  20715. * First byte of size has a special meaning if bit 7 is set.
  20716. *
  20717. * Bit 7 indicates base-256 encoding if set.
  20718. * Bit 6 is the sign bit.
  20719. * Bits 5:0 are most significant value bits.
  20720. */
  20721. $ch = ord($tar_size[0]);
  20722. if ($ch & 0x80) {
  20723. // Full 12-bytes record is required.
  20724. $rec_str = $tar_size . "\x00";
  20725. $size = ($ch & 0x40) ? -1 : 0;
  20726. $size = ($size << 6) | ($ch & 0x3f);
  20727. for ($num_ch = 1; $num_ch < 12; ++$num_ch) {
  20728. $size = ($size * 256) + ord($rec_str[$num_ch]);
  20729. }
  20730. return $size;
  20731. } else {
  20732. return OctDec(trim($tar_size));
  20733. }
  20734. }
  20735. /**
  20736. * Detect and report a malicious file name
  20737. *
  20738. * @param string $file
  20739. *
  20740. * @return bool
  20741. */
  20742. private function _isMaliciousFilename($file)
  20743. {
  20744. if (strpos($file, '://') !== false) {
  20745. return true;
  20746. }
  20747. if (strpos($file, '../') !== false || strpos($file, '..\\') !== false) {
  20748. return true;
  20749. }
  20750. return false;
  20751. }
  20752. /**
  20753. * @param $v_header
  20754. * @return bool
  20755. */
  20756. public function _readLongHeader(&$v_header)
  20757. {
  20758. $v_filename = '';
  20759. $v_filesize = $v_header['size'];
  20760. $n = floor($v_header['size'] / 512);
  20761. for ($i = 0; $i < $n; $i++) {
  20762. $v_content = $this->_readBlock();
  20763. $v_filename .= $v_content;
  20764. }
  20765. if (($v_header['size'] % 512) != 0) {
  20766. $v_content = $this->_readBlock();
  20767. $v_filename .= $v_content;
  20768. }
  20769. // ----- Read the next header
  20770. $v_binary_data = $this->_readBlock();
  20771. if (!$this->_readHeader($v_binary_data, $v_header)) {
  20772. return false;
  20773. }
  20774. $v_filename = rtrim(substr($v_filename, 0, $v_filesize), "\0");
  20775. $v_header['filename'] = $v_filename;
  20776. if ($this->_isMaliciousFilename($v_filename)) {
  20777. $this->_error(
  20778. 'Malicious .tar detected, file "' . $v_filename .
  20779. '" will not install in desired directory tree'
  20780. );
  20781. return false;
  20782. }
  20783. return true;
  20784. }
  20785. /**
  20786. * This method extract from the archive one file identified by $p_filename.
  20787. * The return value is a string with the file content, or null on error.
  20788. *
  20789. * @param string $p_filename The path of the file to extract in a string.
  20790. *
  20791. * @return a string with the file content or null.
  20792. */
  20793. private function _extractInString($p_filename)
  20794. {
  20795. $v_result_str = "";
  20796. while (strlen($v_binary_data = $this->_readBlock()) != 0) {
  20797. if (!$this->_readHeader($v_binary_data, $v_header)) {
  20798. return null;
  20799. }
  20800. if ($v_header['filename'] == '') {
  20801. continue;
  20802. }
  20803. switch ($v_header['typeflag']) {
  20804. case 'L':
  20805. {
  20806. if (!$this->_readLongHeader($v_header)) {
  20807. return null;
  20808. }
  20809. }
  20810. break;
  20811. case 'K':
  20812. {
  20813. $v_link_header = $v_header;
  20814. if (!$this->_readLongHeader($v_link_header)) {
  20815. return null;
  20816. }
  20817. $v_header['link'] = $v_link_header['filename'];
  20818. }
  20819. break;
  20820. }
  20821. if ($v_header['filename'] == $p_filename) {
  20822. if ($v_header['typeflag'] == "5") {
  20823. $this->_error(
  20824. 'Unable to extract in string a directory '
  20825. . 'entry {' . $v_header['filename'] . '}'
  20826. );
  20827. return null;
  20828. } else {
  20829. $n = floor($v_header['size'] / 512);
  20830. for ($i = 0; $i < $n; $i++) {
  20831. $v_result_str .= $this->_readBlock();
  20832. }
  20833. if (($v_header['size'] % 512) != 0) {
  20834. $v_content = $this->_readBlock();
  20835. $v_result_str .= substr(
  20836. $v_content,
  20837. 0,
  20838. ($v_header['size'] % 512)
  20839. );
  20840. }
  20841. return $v_result_str;
  20842. }
  20843. } else {
  20844. $this->_jumpBlock(ceil(($v_header['size'] / 512)));
  20845. }
  20846. }
  20847. return null;
  20848. }
  20849. /**
  20850. * @param string $p_path
  20851. * @param string $p_list_detail
  20852. * @param string $p_mode
  20853. * @param string $p_file_list
  20854. * @param string $p_remove_path
  20855. * @param bool $p_preserve
  20856. * @param bool $p_symlinks
  20857. * @return bool
  20858. */
  20859. public function _extractList(
  20860. $p_path,
  20861. &$p_list_detail,
  20862. $p_mode,
  20863. $p_file_list,
  20864. $p_remove_path,
  20865. $p_preserve = false,
  20866. $p_symlinks = true
  20867. )
  20868. {
  20869. $v_result = true;
  20870. $v_nb = 0;
  20871. $v_extract_all = true;
  20872. $v_listing = false;
  20873. $p_path = $this->_translateWinPath($p_path, false);
  20874. if ($p_path == '' || (substr($p_path, 0, 1) != '/'
  20875. && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))
  20876. ) {
  20877. $p_path = "./" . $p_path;
  20878. }
  20879. $p_remove_path = $this->_translateWinPath($p_remove_path);
  20880. // ----- Look for path to remove format (should end by /)
  20881. if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/')) {
  20882. $p_remove_path .= '/';
  20883. }
  20884. $p_remove_path_size = strlen($p_remove_path);
  20885. switch ($p_mode) {
  20886. case "complete" :
  20887. $v_extract_all = true;
  20888. $v_listing = false;
  20889. break;
  20890. case "partial" :
  20891. $v_extract_all = false;
  20892. $v_listing = false;
  20893. break;
  20894. case "list" :
  20895. $v_extract_all = false;
  20896. $v_listing = true;
  20897. break;
  20898. default :
  20899. $this->_error('Invalid extract mode (' . $p_mode . ')');
  20900. return false;
  20901. }
  20902. clearstatcache();
  20903. while (strlen($v_binary_data = $this->_readBlock()) != 0) {
  20904. $v_extract_file = false;
  20905. $v_extraction_stopped = 0;
  20906. if (!$this->_readHeader($v_binary_data, $v_header)) {
  20907. return false;
  20908. }
  20909. if ($v_header['filename'] == '') {
  20910. continue;
  20911. }
  20912. switch ($v_header['typeflag']) {
  20913. case 'L':
  20914. {
  20915. if (!$this->_readLongHeader($v_header)) {
  20916. return null;
  20917. }
  20918. }
  20919. break;
  20920. case 'K':
  20921. {
  20922. $v_link_header = $v_header;
  20923. if (!$this->_readLongHeader($v_link_header)) {
  20924. return null;
  20925. }
  20926. $v_header['link'] = $v_link_header['filename'];
  20927. }
  20928. break;
  20929. }
  20930. // ignore extended / pax headers
  20931. if ($v_header['typeflag'] == 'x' || $v_header['typeflag'] == 'g') {
  20932. $this->_jumpBlock(ceil(($v_header['size'] / 512)));
  20933. continue;
  20934. }
  20935. if ((!$v_extract_all) && (is_array($p_file_list))) {
  20936. // ----- By default no unzip if the file is not found
  20937. $v_extract_file = false;
  20938. for ($i = 0; $i < sizeof($p_file_list); $i++) {
  20939. // ----- Look if it is a directory
  20940. if (substr($p_file_list[$i], -1) == '/') {
  20941. // ----- Look if the directory is in the filename path
  20942. if ((strlen($v_header['filename']) > strlen($p_file_list[$i]))
  20943. && (substr($v_header['filename'], 0, strlen($p_file_list[$i]))
  20944. == $p_file_list[$i])
  20945. ) {
  20946. $v_extract_file = true;
  20947. break;
  20948. }
  20949. } // ----- It is a file, so compare the file names
  20950. elseif ($p_file_list[$i] == $v_header['filename']) {
  20951. $v_extract_file = true;
  20952. break;
  20953. }
  20954. }
  20955. } else {
  20956. $v_extract_file = true;
  20957. }
  20958. // ----- Look if this file need to be extracted
  20959. if (($v_extract_file) && (!$v_listing)) {
  20960. if (($p_remove_path != '')
  20961. && (substr($v_header['filename'] . '/', 0, $p_remove_path_size)
  20962. == $p_remove_path)
  20963. ) {
  20964. $v_header['filename'] = substr(
  20965. $v_header['filename'],
  20966. $p_remove_path_size
  20967. );
  20968. if ($v_header['filename'] == '') {
  20969. continue;
  20970. }
  20971. }
  20972. if (($p_path != './') && ($p_path != '/')) {
  20973. while (substr($p_path, -1) == '/') {
  20974. $p_path = substr($p_path, 0, strlen($p_path) - 1);
  20975. }
  20976. if (substr($v_header['filename'], 0, 1) == '/') {
  20977. $v_header['filename'] = $p_path . $v_header['filename'];
  20978. } else {
  20979. $v_header['filename'] = $p_path . '/' . $v_header['filename'];
  20980. }
  20981. }
  20982. if (file_exists($v_header['filename'])) {
  20983. if ((@is_dir($v_header['filename']))
  20984. && ($v_header['typeflag'] == '')
  20985. ) {
  20986. $this->_error(
  20987. 'File ' . $v_header['filename']
  20988. . ' already exists as a directory'
  20989. );
  20990. return false;
  20991. }
  20992. if (($this->_isArchive($v_header['filename']))
  20993. && ($v_header['typeflag'] == "5")
  20994. ) {
  20995. $this->_error(
  20996. 'Directory ' . $v_header['filename']
  20997. . ' already exists as a file'
  20998. );
  20999. return false;
  21000. }
  21001. if (!is_writeable($v_header['filename'])) {
  21002. $this->_error(
  21003. 'File ' . $v_header['filename']
  21004. . ' already exists and is write protected'
  21005. );
  21006. return false;
  21007. }
  21008. if (filemtime($v_header['filename']) > $v_header['mtime']) {
  21009. // To be completed : An error or silent no replace ?
  21010. }
  21011. } // ----- Check the directory availability and create it if necessary
  21012. elseif (($v_result
  21013. = $this->_dirCheck(
  21014. ($v_header['typeflag'] == "5"
  21015. ? $v_header['filename']
  21016. : dirname($v_header['filename']))
  21017. )) != 1
  21018. ) {
  21019. $this->_error('Unable to create path for ' . $v_header['filename']);
  21020. return false;
  21021. }
  21022. if ($v_extract_file) {
  21023. if ($v_header['typeflag'] == "5") {
  21024. if (!@file_exists($v_header['filename'])) {
  21025. if (!@mkdir($v_header['filename'], 0775)) {
  21026. $this->_error(
  21027. 'Unable to create directory {'
  21028. . $v_header['filename'] . '}'
  21029. );
  21030. return false;
  21031. }
  21032. }
  21033. } elseif ($v_header['typeflag'] == "2") {
  21034. if (!$p_symlinks) {
  21035. $this->_warning('Symbolic links are not allowed. '
  21036. . 'Unable to extract {'
  21037. . $v_header['filename'] . '}'
  21038. );
  21039. return false;
  21040. }
  21041. $absolute_link = FALSE;
  21042. $link_depth = 0;
  21043. if (strpos($v_header['link'], "/") === 0 || strpos($v_header['link'], ':') !== FALSE) {
  21044. $absolute_link = TRUE;
  21045. }
  21046. else {
  21047. $s_filename = preg_replace('@^' . preg_quote($p_path) . '@', "", $v_header['filename']);
  21048. $s_linkname = str_replace('\\', '/', $v_header['link']);
  21049. foreach (explode("/", $s_filename) as $dir) {
  21050. if ($dir === "..") {
  21051. $link_depth--;
  21052. } elseif ($dir !== "" && $dir !== "." ) {
  21053. $link_depth++;
  21054. }
  21055. }
  21056. foreach (explode("/", $s_linkname) as $dir){
  21057. if ($link_depth <= 0) {
  21058. break;
  21059. }
  21060. if ($dir === "..") {
  21061. $link_depth--;
  21062. } elseif ($dir !== "" && $dir !== ".") {
  21063. $link_depth++;
  21064. }
  21065. }
  21066. }
  21067. if ($absolute_link || $link_depth <= 0) {
  21068. $this->_error(
  21069. 'Out-of-path file extraction {'
  21070. . $v_header['filename'] . ' --> ' .
  21071. $v_header['link'] . '}'
  21072. );
  21073. return false;
  21074. }
  21075. if (@file_exists($v_header['filename'])) {
  21076. @unlink($v_header['filename']);
  21077. }
  21078. if (!@symlink($v_header['link'], $v_header['filename'])) {
  21079. $this->_error(
  21080. 'Unable to extract symbolic link {'
  21081. . $v_header['filename'] . '}'
  21082. );
  21083. return false;
  21084. }
  21085. } else {
  21086. if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
  21087. $this->_error(
  21088. 'Error while opening {' . $v_header['filename']
  21089. . '} in write binary mode'
  21090. );
  21091. return false;
  21092. } else {
  21093. $n = floor($v_header['size'] / 512);
  21094. for ($i = 0; $i < $n; $i++) {
  21095. $v_content = $this->_readBlock();
  21096. fwrite($v_dest_file, $v_content, 512);
  21097. }
  21098. if (($v_header['size'] % 512) != 0) {
  21099. $v_content = $this->_readBlock();
  21100. fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
  21101. }
  21102. @fclose($v_dest_file);
  21103. if ($p_preserve) {
  21104. @chown($v_header['filename'], $v_header['uid']);
  21105. @chgrp($v_header['filename'], $v_header['gid']);
  21106. }
  21107. // ----- Change the file mode, mtime
  21108. @touch($v_header['filename'], $v_header['mtime']);
  21109. if ($v_header['mode'] & 0111) {
  21110. // make file executable, obey umask
  21111. $mode = fileperms($v_header['filename']) | (~umask() & 0111);
  21112. @chmod($v_header['filename'], $mode);
  21113. }
  21114. }
  21115. // ----- Check the file size
  21116. clearstatcache();
  21117. if (!is_file($v_header['filename'])) {
  21118. $this->_error(
  21119. 'Extracted file ' . $v_header['filename']
  21120. . 'does not exist. Archive may be corrupted.'
  21121. );
  21122. return false;
  21123. }
  21124. $filesize = filesize($v_header['filename']);
  21125. if ($filesize != $v_header['size']) {
  21126. $this->_error(
  21127. 'Extracted file ' . $v_header['filename']
  21128. . ' does not have the correct file size \''
  21129. . $filesize
  21130. . '\' (' . $v_header['size']
  21131. . ' expected). Archive may be corrupted.'
  21132. );
  21133. return false;
  21134. }
  21135. }
  21136. } else {
  21137. $this->_jumpBlock(ceil(($v_header['size'] / 512)));
  21138. }
  21139. } else {
  21140. $this->_jumpBlock(ceil(($v_header['size'] / 512)));
  21141. }
  21142. /* TBC : Seems to be unused ...
  21143. if ($this->_compress)
  21144. $v_end_of_file = @gzeof($this->_file);
  21145. else
  21146. $v_end_of_file = @feof($this->_file);
  21147. */
  21148. if ($v_listing || $v_extract_file || $v_extraction_stopped) {
  21149. // ----- Log extracted files
  21150. if (($v_file_dir = dirname($v_header['filename']))
  21151. == $v_header['filename']
  21152. ) {
  21153. $v_file_dir = '';
  21154. }
  21155. if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == '')) {
  21156. $v_file_dir = '/';
  21157. }
  21158. $p_list_detail[$v_nb++] = $v_header;
  21159. if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) {
  21160. return true;
  21161. }
  21162. }
  21163. }
  21164. return true;
  21165. }
  21166. /**
  21167. * @return bool
  21168. */
  21169. public function _openAppend()
  21170. {
  21171. if (filesize($this->_tarname) == 0) {
  21172. return $this->_openWrite();
  21173. }
  21174. if ($this->_compress) {
  21175. $this->_close();
  21176. if (!@rename($this->_tarname, $this->_tarname . ".tmp")) {
  21177. $this->_error(
  21178. 'Error while renaming \'' . $this->_tarname
  21179. . '\' to temporary file \'' . $this->_tarname
  21180. . '.tmp\''
  21181. );
  21182. return false;
  21183. }
  21184. if ($this->_compress_type == 'gz') {
  21185. $v_temp_tar = @gzopen($this->_tarname . ".tmp", "rb");
  21186. } elseif ($this->_compress_type == 'bz2') {
  21187. $v_temp_tar = @bzopen($this->_tarname . ".tmp", "r");
  21188. } elseif ($this->_compress_type == 'lzma2') {
  21189. $v_temp_tar = @xzopen($this->_tarname . ".tmp", "r");
  21190. }
  21191. if ($v_temp_tar == 0) {
  21192. $this->_error(
  21193. 'Unable to open file \'' . $this->_tarname
  21194. . '.tmp\' in binary read mode'
  21195. );
  21196. @rename($this->_tarname . ".tmp", $this->_tarname);
  21197. return false;
  21198. }
  21199. if (!$this->_openWrite()) {
  21200. @rename($this->_tarname . ".tmp", $this->_tarname);
  21201. return false;
  21202. }
  21203. if ($this->_compress_type == 'gz') {
  21204. $end_blocks = 0;
  21205. while (!@gzeof($v_temp_tar)) {
  21206. $v_buffer = @gzread($v_temp_tar, 512);
  21207. if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
  21208. $end_blocks++;
  21209. // do not copy end blocks, we will re-make them
  21210. // after appending
  21211. continue;
  21212. } elseif ($end_blocks > 0) {
  21213. for ($i = 0; $i < $end_blocks; $i++) {
  21214. $this->_writeBlock(ARCHIVE_TAR_END_BLOCK);
  21215. }
  21216. $end_blocks = 0;
  21217. }
  21218. $v_binary_data = pack("a512", $v_buffer);
  21219. $this->_writeBlock($v_binary_data);
  21220. }
  21221. @gzclose($v_temp_tar);
  21222. } elseif ($this->_compress_type == 'bz2') {
  21223. $end_blocks = 0;
  21224. while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) {
  21225. if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
  21226. $end_blocks++;
  21227. // do not copy end blocks, we will re-make them
  21228. // after appending
  21229. continue;
  21230. } elseif ($end_blocks > 0) {
  21231. for ($i = 0; $i < $end_blocks; $i++) {
  21232. $this->_writeBlock(ARCHIVE_TAR_END_BLOCK);
  21233. }
  21234. $end_blocks = 0;
  21235. }
  21236. $v_binary_data = pack("a512", $v_buffer);
  21237. $this->_writeBlock($v_binary_data);
  21238. }
  21239. @bzclose($v_temp_tar);
  21240. } elseif ($this->_compress_type == 'lzma2') {
  21241. $end_blocks = 0;
  21242. while (strlen($v_buffer = @xzread($v_temp_tar, 512)) > 0) {
  21243. if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
  21244. $end_blocks++;
  21245. // do not copy end blocks, we will re-make them
  21246. // after appending
  21247. continue;
  21248. } elseif ($end_blocks > 0) {
  21249. for ($i = 0; $i < $end_blocks; $i++) {
  21250. $this->_writeBlock(ARCHIVE_TAR_END_BLOCK);
  21251. }
  21252. $end_blocks = 0;
  21253. }
  21254. $v_binary_data = pack("a512", $v_buffer);
  21255. $this->_writeBlock($v_binary_data);
  21256. }
  21257. @xzclose($v_temp_tar);
  21258. }
  21259. if (!@unlink($this->_tarname . ".tmp")) {
  21260. $this->_error(
  21261. 'Error while deleting temporary file \''
  21262. . $this->_tarname . '.tmp\''
  21263. );
  21264. }
  21265. } else {
  21266. // ----- For not compressed tar, just add files before the last
  21267. // one or two 512 bytes block
  21268. if (!$this->_openReadWrite()) {
  21269. return false;
  21270. }
  21271. clearstatcache();
  21272. $v_size = filesize($this->_tarname);
  21273. // We might have zero, one or two end blocks.
  21274. // The standard is two, but we should try to handle
  21275. // other cases.
  21276. fseek($this->_file, $v_size - 1024);
  21277. if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
  21278. fseek($this->_file, $v_size - 1024);
  21279. } elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
  21280. fseek($this->_file, $v_size - 512);
  21281. }
  21282. }
  21283. return true;
  21284. }
  21285. /**
  21286. * @param $p_filelist
  21287. * @param string $p_add_dir
  21288. * @param string $p_remove_dir
  21289. * @return bool
  21290. */
  21291. public function _append($p_filelist, $p_add_dir = '', $p_remove_dir = '')
  21292. {
  21293. if (!$this->_openAppend()) {
  21294. return false;
  21295. }
  21296. if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir)) {
  21297. $this->_writeFooter();
  21298. }
  21299. $this->_close();
  21300. return true;
  21301. }
  21302. /**
  21303. * Check if a directory exists and create it (including parent
  21304. * dirs) if not.
  21305. *
  21306. * @param string $p_dir directory to check
  21307. *
  21308. * @return bool true if the directory exists or was created
  21309. */
  21310. public function _dirCheck($p_dir)
  21311. {
  21312. clearstatcache();
  21313. if ((@is_dir($p_dir)) || ($p_dir == '')) {
  21314. return true;
  21315. }
  21316. $p_parent_dir = dirname($p_dir);
  21317. if (($p_parent_dir != $p_dir) &&
  21318. ($p_parent_dir != '') &&
  21319. (!$this->_dirCheck($p_parent_dir))
  21320. ) {
  21321. return false;
  21322. }
  21323. if (!@mkdir($p_dir, 0775)) {
  21324. $this->_error("Unable to create directory '$p_dir'");
  21325. return false;
  21326. }
  21327. return true;
  21328. }
  21329. /**
  21330. * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar",
  21331. * rand emove double slashes.
  21332. *
  21333. * @param string $p_dir path to reduce
  21334. *
  21335. * @return string reduced path
  21336. */
  21337. private function _pathReduction($p_dir)
  21338. {
  21339. $v_result = '';
  21340. // ----- Look for not empty path
  21341. if ($p_dir != '') {
  21342. // ----- Explode path by directory names
  21343. $v_list = explode('/', $p_dir);
  21344. // ----- Study directories from last to first
  21345. for ($i = sizeof($v_list) - 1; $i >= 0; $i--) {
  21346. // ----- Look for current path
  21347. if ($v_list[$i] == ".") {
  21348. // ----- Ignore this directory
  21349. // Should be the first $i=0, but no check is done
  21350. } else {
  21351. if ($v_list[$i] == "..") {
  21352. // ----- Ignore it and ignore the $i-1
  21353. $i--;
  21354. } else {
  21355. if (($v_list[$i] == '')
  21356. && ($i != (sizeof($v_list) - 1))
  21357. && ($i != 0)
  21358. ) {
  21359. // ----- Ignore only the double '//' in path,
  21360. // but not the first and last /
  21361. } else {
  21362. $v_result = $v_list[$i] . ($i != (sizeof($v_list) - 1) ? '/'
  21363. . $v_result : '');
  21364. }
  21365. }
  21366. }
  21367. }
  21368. }
  21369. if (defined('OS_WINDOWS') && OS_WINDOWS) {
  21370. $v_result = strtr($v_result, '\\', '/');
  21371. }
  21372. return $v_result;
  21373. }
  21374. /**
  21375. * @param $p_path
  21376. * @param bool $p_remove_disk_letter
  21377. * @return string
  21378. */
  21379. public function _translateWinPath($p_path, $p_remove_disk_letter = true)
  21380. {
  21381. if (defined('OS_WINDOWS') && OS_WINDOWS) {
  21382. // ----- Look for potential disk letter
  21383. if (($p_remove_disk_letter)
  21384. && (($v_position = strpos($p_path, ':')) != false)
  21385. ) {
  21386. $p_path = substr($p_path, $v_position + 1);
  21387. }
  21388. // ----- Change potential windows directory separator
  21389. if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0, 1) == '\\')) {
  21390. $p_path = strtr($p_path, '\\', '/');
  21391. }
  21392. }
  21393. return $p_path;
  21394. }
  21395. }
  21396. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Archive_Tar-1.5.0/docs/Archive_Tar.txt��������������������������������������������������������������0000644�0001750�0001750�00000045246�14575343342�017461� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Documentation for class Archive_Tar
  21397. ===================================
  21398. Last update : 2001-08-15
  21399. Overview :
  21400. ----------
  21401. The Archive_Tar class helps in creating and managing GNU TAR format
  21402. files compressed by GNU ZIP or not.
  21403. The class offers basic functions like creating an archive, adding
  21404. files in the archive, extracting files from the archive and listing
  21405. the archive content.
  21406. It also provide advanced functions that allow the adding and
  21407. extraction of files with path manipulation.
  21408. Sample :
  21409. --------
  21410. // ----- Creating the object (uncompressed archive)
  21411. $tar_object = new Archive_Tar("tarname.tar");
  21412. $tar_object->setErrorHandling(PEAR_ERROR_PRINT);
  21413. // ----- Creating the archive
  21414. $v_list[0]="file.txt";
  21415. $v_list[1]="data/";
  21416. $v_list[2]="file.log";
  21417. $tar_object->create($v_list);
  21418. // ----- Adding files
  21419. $v_list[0]="dev/file.txt";
  21420. $v_list[1]="dev/data/";
  21421. $v_list[2]="log/file.log";
  21422. $tar_object->add($v_list);
  21423. // ----- Adding more files
  21424. $tar_object->add("release/newfile.log release/readme.txt");
  21425. // ----- Listing the content
  21426. if (($v_list = $tar_object->listContent()) != 0)
  21427. for ($i=0; $i<sizeof($v_list); $i++)
  21428. {
  21429. echo "Filename :'".$v_list[$i][filename]."'<br>";
  21430. echo " .size :'".$v_list[$i][size]."'<br>";
  21431. echo " .mtime :'".$v_list[$i][mtime]."' (".date("l dS of F Y h:i:s A", $v_list[$i][mtime]).")<br>";
  21432. echo " .mode :'".$v_list[$i][mode]."'<br>";
  21433. echo " .uid :'".$v_list[$i][uid]."'<br>";
  21434. echo " .gid :'".$v_list[$i][gid]."'<br>";
  21435. echo " .typeflag :'".$v_list[$i][typeflag]."'<br>";
  21436. }
  21437. // ----- Extracting the archive in directory "install"
  21438. $tar_object->extract("install");
  21439. Public arguments :
  21440. ------------------
  21441. None
  21442. Public Methods :
  21443. ----------------
  21444. Method : Archive_Tar($p_tarname, $compress = null)
  21445. Description :
  21446. Archive_Tar Class constructor. This flavour of the constructor only
  21447. declare a new Archive_Tar object, identifying it by the name of the
  21448. tar file.
  21449. If the compress argument is set the tar will be read or created as a
  21450. gzip or bz2 compressed TAR file.
  21451. Arguments :
  21452. $p_tarname : A valid filename for the tar archive file.
  21453. $p_compress : can be null, 'gz' or 'bz2'. For
  21454. compatibility reason it can also be true. This
  21455. parameter indicates if gzip or bz2 compression
  21456. is required.
  21457. Return value :
  21458. The Archive_Tar object.
  21459. Sample :
  21460. $tar_object = new Archive_Tar("tarname.tar");
  21461. $tar_object_compressed = new Archive_Tar("tarname.tgz", true);
  21462. How it works :
  21463. Initialize the object.
  21464. Method : create($p_filelist)
  21465. Description :
  21466. This method creates the archive file and add the files / directories
  21467. that are listed in $p_filelist.
  21468. If the file already exists and is writable, it is replaced by the
  21469. new tar. It is a create and not an add. If the file exists and is
  21470. read-only or is a directory it is not replaced. The method return
  21471. false and a PEAR error text.
  21472. The $p_filelist parameter can be an array of string, each string
  21473. representing a filename or a directory name with their path if
  21474. needed. It can also be a single string with names separated by a
  21475. single blank.
  21476. See also createModify() method for more details.
  21477. Arguments :
  21478. $p_filelist : An array of filenames and directory names, or a single
  21479. string with names separated by a single blank space.
  21480. Return value :
  21481. true on success, false on error.
  21482. Sample 1 :
  21483. $tar_object = new Archive_Tar("tarname.tar");
  21484. $tar_object->setErrorHandling(PEAR_ERROR_PRINT); // Optional error handling
  21485. $v_list[0]="file.txt";
  21486. $v_list[1]="data/"; (Optional '/' at the end)
  21487. $v_list[2]="file.log";
  21488. $tar_object->create($v_list);
  21489. Sample 2 :
  21490. $tar_object = new Archive_Tar("tarname.tar");
  21491. $tar_object->setErrorHandling(PEAR_ERROR_PRINT); // Optional error handling
  21492. $tar_object->create("file.txt data/ file.log");
  21493. How it works :
  21494. Just calling the createModify() method with the right parameters.
  21495. Method : createModify($p_filelist, $p_add_dir, $p_remove_dir = "")
  21496. Description :
  21497. This method creates the archive file and add the files / directories
  21498. that are listed in $p_filelist.
  21499. If the file already exists and is writable, it is replaced by the
  21500. new tar. It is a create and not an add. If the file exists and is
  21501. read-only or is a directory it is not replaced. The method return
  21502. false and a PEAR error text.
  21503. The $p_filelist parameter can be an array of string, each string
  21504. representing a filename or a directory name with their path if
  21505. needed. It can also be a single string with names separated by a
  21506. single blank.
  21507. The path indicated in $p_remove_dir will be removed from the
  21508. memorized path of each file / directory listed when this path
  21509. exists. By default nothing is removed (empty path "")
  21510. The path indicated in $p_add_dir will be added at the beginning of
  21511. the memorized path of each file / directory listed. However it can
  21512. be set to empty "". The adding of a path is done after the removing
  21513. of path.
  21514. The path add/remove ability enables the user to prepare an archive
  21515. for extraction in a different path than the origin files are.
  21516. See also addModify() method for file adding properties.
  21517. Arguments :
  21518. $p_filelist : An array of filenames and directory names, or a single
  21519. string with names separated by a single blank space.
  21520. $p_add_dir : A string which contains a path to be added to the
  21521. memorized path of each element in the list.
  21522. $p_remove_dir : A string which contains a path to be removed from
  21523. the memorized path of each element in the list, when
  21524. relevant.
  21525. Return value :
  21526. true on success, false on error.
  21527. Sample 1 :
  21528. $tar_object = new Archive_Tar("tarname.tar");
  21529. $tar_object->setErrorHandling(PEAR_ERROR_PRINT); // Optional error handling
  21530. $v_list[0]="file.txt";
  21531. $v_list[1]="data/"; (Optional '/' at the end)
  21532. $v_list[2]="file.log";
  21533. $tar_object->createModify($v_list, "install");
  21534. // files are stored in the archive as :
  21535. // install/file.txt
  21536. // install/data
  21537. // install/data/file1.txt
  21538. // install/data/... all the files and sub-dirs of data/
  21539. // install/file.log
  21540. Sample 2 :
  21541. $tar_object = new Archive_Tar("tarname.tar");
  21542. $tar_object->setErrorHandling(PEAR_ERROR_PRINT); // Optional error handling
  21543. $v_list[0]="dev/file.txt";
  21544. $v_list[1]="dev/data/"; (Optional '/' at the end)
  21545. $v_list[2]="log/file.log";
  21546. $tar_object->createModify($v_list, "install", "dev");
  21547. // files are stored in the archive as :
  21548. // install/file.txt
  21549. // install/data
  21550. // install/data/file1.txt
  21551. // install/data/... all the files and sub-dirs of data/
  21552. // install/log/file.log
  21553. How it works :
  21554. Open the file in write mode (erasing the existing one if one),
  21555. call the _addList() method for adding the files in an empty archive,
  21556. add the tar footer (512 bytes block), close the tar file.
  21557. Method : addModify($p_filelist, $p_add_dir, $p_remove_dir="")
  21558. Description :
  21559. This method add the files / directories listed in $p_filelist at the
  21560. end of the existing archive. If the archive does not yet exists it
  21561. is created.
  21562. The $p_filelist parameter can be an array of string, each string
  21563. representing a filename or a directory name with their path if
  21564. needed. It can also be a single string with names separated by a
  21565. single blank.
  21566. The path indicated in $p_remove_dir will be removed from the
  21567. memorized path of each file / directory listed when this path
  21568. exists. By default nothing is removed (empty path "")
  21569. The path indicated in $p_add_dir will be added at the beginning of
  21570. the memorized path of each file / directory listed. However it can
  21571. be set to empty "". The adding of a path is done after the removing
  21572. of path.
  21573. The path add/remove ability enables the user to prepare an archive
  21574. for extraction in a different path than the origin files are.
  21575. If a file/dir is already in the archive it will only be added at the
  21576. end of the archive. There is no update of the existing archived
  21577. file/dir. However while extracting the archive, the last file will
  21578. replace the first one. This results in a none optimization of the
  21579. archive size.
  21580. If a file/dir does not exist the file/dir is ignored. However an
  21581. error text is send to PEAR error.
  21582. If a file/dir is not readable the file/dir is ignored. However an
  21583. error text is send to PEAR error.
  21584. If the resulting filename/dirname (after the add/remove option or
  21585. not) string is greater than 99 char, the file/dir is
  21586. ignored. However an error text is send to PEAR error.
  21587. Arguments :
  21588. $p_filelist : An array of filenames and directory names, or a single
  21589. string with names separated by a single blank space.
  21590. $p_add_dir : A string which contains a path to be added to the
  21591. memorized path of each element in the list.
  21592. $p_remove_dir : A string which contains a path to be removed from
  21593. the memorized path of each element in the list, when
  21594. relevant.
  21595. Return value :
  21596. true on success, false on error.
  21597. Sample 1 :
  21598. $tar_object = new Archive_Tar("tarname.tar");
  21599. [...]
  21600. $v_list[0]="dev/file.txt";
  21601. $v_list[1]="dev/data/"; (Optional '/' at the end)
  21602. $v_list[2]="log/file.log";
  21603. $tar_object->addModify($v_list, "install");
  21604. // files are stored in the archive as :
  21605. // install/file.txt
  21606. // install/data
  21607. // install/data/file1.txt
  21608. // install/data/... all the files and sub-dirs of data/
  21609. // install/file.log
  21610. Sample 2 :
  21611. $tar_object = new Archive_Tar("tarname.tar");
  21612. [...]
  21613. $v_list[0]="dev/file.txt";
  21614. $v_list[1]="dev/data/"; (Optional '/' at the end)
  21615. $v_list[2]="log/file.log";
  21616. $tar_object->addModify($v_list, "install", "dev");
  21617. // files are stored in the archive as :
  21618. // install/file.txt
  21619. // install/data
  21620. // install/data/file1.txt
  21621. // install/data/... all the files and sub-dirs of data/
  21622. // install/log/file.log
  21623. How it works :
  21624. If the archive does not exists it create it and add the files.
  21625. If the archive does exists and is not compressed, it open it, jump
  21626. before the last empty 512 bytes block (tar footer) and add the files
  21627. at this point.
  21628. If the archive does exists and is compressed, a temporary copy file
  21629. is created. This temporary file is then 'gzip' read block by block
  21630. until the last empty block. The new files are then added in the
  21631. compressed file.
  21632. The adding of files is done by going through the file/dir list,
  21633. adding files per files, in a recursive way through the
  21634. directory. Each time a path need to be added/removed it is done
  21635. before writing the file header in the archive.
  21636. Method : add($p_filelist)
  21637. Description :
  21638. This method add the files / directories listed in $p_filelist at the
  21639. end of the existing archive. If the archive does not yet exists it
  21640. is created.
  21641. The $p_filelist parameter can be an array of string, each string
  21642. representing a filename or a directory name with their path if
  21643. needed. It can also be a single string with names separated by a
  21644. single blank.
  21645. See addModify() method for details and limitations.
  21646. Arguments :
  21647. $p_filelist : An array of filenames and directory names, or a single
  21648. string with names separated by a single blank space.
  21649. Return value :
  21650. true on success, false on error.
  21651. Sample 1 :
  21652. $tar_object = new Archive_Tar("tarname.tar");
  21653. [...]
  21654. $v_list[0]="dev/file.txt";
  21655. $v_list[1]="dev/data/"; (Optional '/' at the end)
  21656. $v_list[2]="log/file.log";
  21657. $tar_object->add($v_list);
  21658. Sample 2 :
  21659. $tar_object = new Archive_Tar("tarname.tgz", true);
  21660. [...]
  21661. $v_list[0]="dev/file.txt";
  21662. $v_list[1]="dev/data/"; (Optional '/' at the end)
  21663. $v_list[2]="log/file.log";
  21664. $tar_object->add($v_list);
  21665. How it works :
  21666. Simply call the addModify() method with the right parameters.
  21667. Method : addString($p_filename, $p_string, $p_datetime, $p_params)
  21668. Description :
  21669. This method add a single string as a file at the
  21670. end of the existing archive. If the archive does not yet exists it
  21671. is created.
  21672. Arguments :
  21673. $p_filename : A string which contains the full filename path
  21674. that will be associated with the string.
  21675. $p_string : The content of the file added in the archive.
  21676. $p_datetime : (Optional) Timestamp of the file (default = now)
  21677. $p_params : (Optional) Various file metadata:
  21678. stamp - As above, timestamp of the file
  21679. mode - UNIX-style permissions (default 0600)
  21680. type - Is this a regular file or link (see TAR
  21681. format spec for how to create a hard/symlink)
  21682. uid - UNIX-style user ID (default 0 = root)
  21683. gid - UNIX-style group ID (default 0 = root)
  21684. Return value :
  21685. true on success, false on error.
  21686. Sample 1 :
  21687. $v_archive = & new Archive_Tar($p_filename);
  21688. $v_archive->setErrorHandling(PEAR_ERROR_PRINT);
  21689. $v_result = $v_archive->addString('data/test.txt', 'This is the text of the string');
  21690. $v_result = $v_archive->addString(
  21691. 'data/test.sh',
  21692. "#!/bin/sh\necho 'Hello'",
  21693. time(),
  21694. array( "mode" => 0755, "uid" => 34 )
  21695. );
  21696. Method : extract($p_path = "")
  21697. Description :
  21698. This method extract all the content of the archive in the directory
  21699. indicated by $p_path.If $p_path is optional, if not set the archive
  21700. is extracted in the current directory.
  21701. While extracting a file, if the directory path does not exists it is
  21702. created.
  21703. See extractModify() for details and limitations.
  21704. Arguments :
  21705. $p_path : Optional path where the files/dir need to by extracted.
  21706. Return value :
  21707. true on success, false on error.
  21708. Sample :
  21709. $tar_object = new Archive_Tar("tarname.tar");
  21710. $tar_object->extract();
  21711. How it works :
  21712. Simply call the extractModify() method with appropriate parameters.
  21713. Method : extractModify($p_path, $p_remove_path)
  21714. Description :
  21715. This method extract all the content of the archive in the directory
  21716. indicated by $p_path. When relevant the memorized path of the
  21717. files/dir can be modified by removing the $p_remove_path path at the
  21718. beginning of the file/dir path.
  21719. While extracting a file, if the directory path does not exists it is
  21720. created.
  21721. While extracting a file, if the file already exists it is replaced
  21722. without looking for last modification date.
  21723. While extracting a file, if the file already exists and is write
  21724. protected, the extraction is aborted.
  21725. While extracting a file, if a directory with the same name already
  21726. exists, the extraction is aborted.
  21727. While extracting a directory, if a file with the same name already
  21728. exists, the extraction is aborted.
  21729. While extracting a file/directory if the destination directory exist
  21730. and is write protected, or does not exist but can not be created,
  21731. the extraction is aborted.
  21732. If after extraction an extracted file does not show the correct
  21733. stored file size, the extraction is aborted.
  21734. When the extraction is aborted, a PEAR error text is set and false
  21735. is returned. However the result can be a partial extraction that may
  21736. need to be manually cleaned.
  21737. Arguments :
  21738. $p_path : The path of the directory where the files/dir need to by
  21739. extracted.
  21740. $p_remove_path : Part of the memorized path that can be removed if
  21741. present at the beginning of the file/dir path.
  21742. Return value :
  21743. true on success, false on error.
  21744. Sample :
  21745. // Imagine tarname.tar with files :
  21746. // dev/data/file.txt
  21747. // dev/data/log.txt
  21748. // readme.txt
  21749. $tar_object = new Archive_Tar("tarname.tar");
  21750. $tar_object->extractModify("install", "dev");
  21751. // Files will be extracted there :
  21752. // install/data/file.txt
  21753. // install/data/log.txt
  21754. // install/readme.txt
  21755. How it works :
  21756. Open the archive and call a more generic function that can extract
  21757. only a part of the archive or all the archive.
  21758. See extractList() method for more details.
  21759. Method : extractInString($p_filename)
  21760. Description :
  21761. This method extract from the archive one file identified by $p_filename.
  21762. The return value is a string with the file content, or NULL on error.
  21763. Arguments :
  21764. $p_filename : The path of the file to extract in a string.
  21765. Return value :
  21766. a string with the file content or NULL.
  21767. Sample :
  21768. // Imagine tarname.tar with files :
  21769. // dev/data/file.txt
  21770. // dev/data/log.txt
  21771. // dev/readme.txt
  21772. $v_archive = & new Archive_Tar('tarname.tar');
  21773. $v_archive->setErrorHandling(PEAR_ERROR_PRINT);
  21774. $v_string = $v_archive->extractInString('dev/readme.txt');
  21775. echo $v_string;
  21776. Method : listContent()
  21777. Description :
  21778. This method returns an array of arrays that describe each
  21779. file/directory present in the archive.
  21780. The array is not sorted, so it show the position of the file in the
  21781. archive.
  21782. The file informations are :
  21783. $file[filename] : Name and path of the file/dir.
  21784. $file[mode] : File permissions (result of fileperms())
  21785. $file[uid] : user id
  21786. $file[gid] : group id
  21787. $file[size] : filesize
  21788. $file[mtime] : Last modification time (result of filemtime())
  21789. $file[typeflag] : "" for file, "5" for directory
  21790. Arguments :
  21791. Return value :
  21792. An array of arrays or 0 on error.
  21793. Sample :
  21794. $tar_object = new Archive_Tar("tarname.tar");
  21795. if (($v_list = $tar_object->listContent()) != 0)
  21796. for ($i=0; $i<sizeof($v_list); $i++)
  21797. {
  21798. echo "Filename :'".$v_list[$i][filename]."'<br>";
  21799. echo " .size :'".$v_list[$i][size]."'<br>";
  21800. echo " .mtime :'".$v_list[$i][mtime]."' (".
  21801. date("l dS of F Y h:i:s A", $v_list[$i][mtime]).")<br>";
  21802. echo " .mode :'".$v_list[$i][mode]."'<br>";
  21803. echo " .uid :'".$v_list[$i][uid]."'<br>";
  21804. echo " .gid :'".$v_list[$i][gid]."'<br>";
  21805. echo " .typeflag :'".$v_list[$i][typeflag]."'<br>";
  21806. }
  21807. How it works :
  21808. Call the same function as an extract however with a flag to only go
  21809. through the archive without extracting the files.
  21810. Method : extractList($p_filelist, $p_path = "", $p_remove_path = "")
  21811. Description :
  21812. This method extract from the archive only the files indicated in the
  21813. $p_filelist. These files are extracted in the current directory or
  21814. in the directory indicated by the optional $p_path parameter.
  21815. If indicated the $p_remove_path can be used in the same way as it is
  21816. used in extractModify() method.
  21817. Arguments :
  21818. $p_filelist : An array of filenames and directory names, or a single
  21819. string with names separated by a single blank space.
  21820. $p_path : The path of the directory where the files/dir need to by
  21821. extracted.
  21822. $p_remove_path : Part of the memorized path that can be removed if
  21823. present at the beginning of the file/dir path.
  21824. Return value :
  21825. true on success, false on error.
  21826. Sample :
  21827. // Imagine tarname.tar with files :
  21828. // dev/data/file.txt
  21829. // dev/data/log.txt
  21830. // readme.txt
  21831. $tar_object = new Archive_Tar("tarname.tar");
  21832. $tar_object->extractList("dev/data/file.txt readme.txt", "install",
  21833. "dev");
  21834. // Files will be extracted there :
  21835. // install/data/file.txt
  21836. // install/readme.txt
  21837. How it works :
  21838. Go through the archive and extract only the files present in the
  21839. list.
  21840. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package.sig�����������������������������������������������������������������������������������������0000664�0001750�0001750�00000000303�14575343370�013036� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������-----BEGIN PGP SIGNATURE-----
  21841. iF0EABECAB0WIQQQ9oz3P4qkJvYXdSlyoyG6wkXxdQUCZfXG+AAKCRByoyG6wkXx
  21842. dWGFAJ47mleOupFDI3l/aiKTj3zafih36gCgosA9KxDa87XLExsxHdS2CgNPNJA=
  21843. =XWRy
  21844. -----END PGP SIGNATURE-----
  21845. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package.xml�����������������������������������������������������������������������������������������0000644�0001750�0001750�00000016655�13565302573�013071� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?>
  21846. <package packagerversion="1.10.10" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
  21847. <name>Console_Getopt</name>
  21848. <channel>pear.php.net</channel>
  21849. <summary>Command-line option parser</summary>
  21850. <description>This is a PHP implementation of &quot;getopt&quot; supporting both
  21851. short and long options.</description>
  21852. <lead>
  21853. <name>Andrei Zmievski</name>
  21854. <user>andrei</user>
  21855. <email>andrei@php.net</email>
  21856. <active>no</active>
  21857. </lead>
  21858. <developer>
  21859. <name>Stig Bakken</name>
  21860. <user>ssb</user>
  21861. <email>stig@php.net</email>
  21862. <active>no</active>
  21863. </developer>
  21864. <helper>
  21865. <name>Greg Beaver</name>
  21866. <user>cellog</user>
  21867. <email>cellog@php.net</email>
  21868. <active>no</active>
  21869. </helper>
  21870. <date>2019-11-20</date>
  21871. <time>18:27:07</time>
  21872. <version>
  21873. <release>1.4.3</release>
  21874. <api>1.4.0</api>
  21875. </version>
  21876. <stability>
  21877. <release>stable</release>
  21878. <api>stable</api>
  21879. </stability>
  21880. <license uri="http://opensource.org/licenses/bsd-license.php">BSD-2-Clause</license>
  21881. <notes>
  21882. * PR #4: Fix PHP 7.4 deprecation: array/string curly braces access
  21883. * PR #5: fix phplint warnings
  21884. </notes>
  21885. <contents>
  21886. <dir name="/">
  21887. <file md5sum="63da5909aa85a0eb76e0ad0b5e00811a" name="Console/Getopt.php" role="php" />
  21888. <file md5sum="5a6fa63ce6f2370cdad11dc24a5addd0" name="tests/001-getopt.phpt" role="test" />
  21889. <file md5sum="7540b630cb8e7bfd8bb06fb65a010ae9" name="tests/bug10557.phpt" role="test" />
  21890. <file md5sum="e469de3628de85779118103b3248a44f" name="tests/bug11068.phpt" role="test" />
  21891. <file md5sum="cdc108b084ad8e82eeb2417f04b49ec8" name="tests/bug13140.phpt" role="test" />
  21892. </dir>
  21893. </contents>
  21894. <compatible>
  21895. <name>PEAR</name>
  21896. <channel>pear.php.net</channel>
  21897. <min>1.4.0</min>
  21898. <max>1.999.999</max>
  21899. </compatible>
  21900. <dependencies>
  21901. <required>
  21902. <php>
  21903. <min>5.4.0</min>
  21904. </php>
  21905. <pearinstaller>
  21906. <min>1.8.0</min>
  21907. </pearinstaller>
  21908. </required>
  21909. </dependencies>
  21910. <phprelease />
  21911. <changelog>
  21912. <release>
  21913. <date>2019-11-20</date>
  21914. <version>
  21915. <release>1.4.3</release>
  21916. <api>1.4.0</api>
  21917. </version>
  21918. <stability>
  21919. <release>stable</release>
  21920. <api>stable</api>
  21921. </stability>
  21922. <license uri="http://opensource.org/licenses/bsd-license.php">BSD-2-Clause</license>
  21923. <notes>
  21924. * PR #4: Fix PHP 7.4 deprecation: array/string curly braces access
  21925. * PR #5: fix phplint warnings
  21926. </notes>
  21927. </release>
  21928. <release>
  21929. <date>2019-02-06</date>
  21930. <version>
  21931. <release>1.4.2</release>
  21932. <api>1.4.0</api>
  21933. </version>
  21934. <stability>
  21935. <release>stable</release>
  21936. <api>stable</api>
  21937. </stability>
  21938. <license uri="http://opensource.org/licenses/bsd-license.php">BSD-2-Clause</license>
  21939. <notes>
  21940. * Remove use of each(), which is removed in PHP 8
  21941. </notes>
  21942. </release>
  21943. <release>
  21944. <date>2015-07-20</date>
  21945. <version>
  21946. <release>1.4.1</release>
  21947. <api>1.4.0</api>
  21948. </version>
  21949. <stability>
  21950. <release>stable</release>
  21951. <api>stable</api>
  21952. </stability>
  21953. <license uri="http://opensource.org/licenses/bsd-license.php">BSD-2-Clause</license>
  21954. <notes>
  21955. * Fix unit test on PHP 7 [cweiske]
  21956. </notes>
  21957. </release>
  21958. <release>
  21959. <date>2015-02-22</date>
  21960. <version>
  21961. <release>1.4.0</release>
  21962. <api>1.4.0</api>
  21963. </version>
  21964. <stability>
  21965. <release>stable</release>
  21966. <api>stable</api>
  21967. </stability>
  21968. <license uri="http://opensource.org/licenses/bsd-license.php">BSD-2-Clause</license>
  21969. <notes>
  21970. * Change license to BSD-2-Clause
  21971. * Set minimum PHP version to 5.4.0
  21972. * Mark static methods with &quot;static&quot; keyword
  21973. </notes>
  21974. </release>
  21975. <release>
  21976. <date>2011-03-07</date>
  21977. <version>
  21978. <release>1.3.1</release>
  21979. <api>1.3.0</api>
  21980. </version>
  21981. <stability>
  21982. <release>stable</release>
  21983. <api>stable</api>
  21984. </stability>
  21985. <license uri="http://www.php.net/license">PHP License</license>
  21986. <notes>
  21987. * Change the minimum PEAR installer dep to be lower
  21988. </notes>
  21989. </release>
  21990. <release>
  21991. <date>2010-12-11</date>
  21992. <time>20:20:13</time>
  21993. <version>
  21994. <release>1.3.0</release>
  21995. <api>1.3.0</api>
  21996. </version>
  21997. <stability>
  21998. <release>stable</release>
  21999. <api>stable</api>
  22000. </stability>
  22001. <license uri="http://www.php.net/license">PHP License</license>
  22002. <notes>
  22003. * Implement Request #13140: [PATCH] to skip unknown parameters. [patch by rquadling, improved on by dufuz]
  22004. </notes>
  22005. </release>
  22006. <release>
  22007. <date>2007-06-12</date>
  22008. <version>
  22009. <release>1.2.3</release>
  22010. <api>1.2.1</api>
  22011. </version>
  22012. <stability>
  22013. <release>stable</release>
  22014. <api>stable</api>
  22015. </stability>
  22016. <license uri="http://www.php.net/license">PHP License</license>
  22017. <notes>
  22018. * fix Bug #11068: No way to read plain &quot;-&quot; option [cardoe]
  22019. </notes>
  22020. </release>
  22021. <release>
  22022. <version>
  22023. <release>1.2.2</release>
  22024. <api>1.2.1</api>
  22025. </version>
  22026. <stability>
  22027. <release>stable</release>
  22028. <api>stable</api>
  22029. </stability>
  22030. <date>2007-02-17</date>
  22031. <license uri="http://www.php.net/license">PHP License</license>
  22032. <notes>
  22033. * fix Bug #4475: An ambiguous error occurred when specifying similar longoption name.
  22034. * fix Bug #10055: Not failing properly on short options missing required values
  22035. </notes>
  22036. </release>
  22037. <release>
  22038. <version>
  22039. <release>1.2.1</release>
  22040. <api>1.2.1</api>
  22041. </version>
  22042. <stability>
  22043. <release>stable</release>
  22044. <api>stable</api>
  22045. </stability>
  22046. <date>2006-12-08</date>
  22047. <license uri="http://www.php.net/license">PHP License</license>
  22048. <notes>
  22049. Fixed bugs #4448 (Long parameter values truncated with longoption parameter) and #7444 (Trailing spaces after php closing tag)
  22050. </notes>
  22051. </release>
  22052. <release>
  22053. <version>
  22054. <release>1.2</release>
  22055. <api>1.2</api>
  22056. </version>
  22057. <stability>
  22058. <release>stable</release>
  22059. <api>stable</api>
  22060. </stability>
  22061. <date>2003-12-11</date>
  22062. <license uri="http://www.php.net/license">PHP License</license>
  22063. <notes>
  22064. Fix to preserve BC with 1.0 and allow correct behaviour for new users
  22065. </notes>
  22066. </release>
  22067. <release>
  22068. <version>
  22069. <release>1.0</release>
  22070. <api>1.0</api>
  22071. </version>
  22072. <stability>
  22073. <release>stable</release>
  22074. <api>stable</api>
  22075. </stability>
  22076. <date>2002-09-13</date>
  22077. <license uri="http://www.php.net/license">PHP License</license>
  22078. <notes>
  22079. Stable release
  22080. </notes>
  22081. </release>
  22082. <release>
  22083. <version>
  22084. <release>0.11</release>
  22085. <api>0.11</api>
  22086. </version>
  22087. <stability>
  22088. <release>beta</release>
  22089. <api>beta</api>
  22090. </stability>
  22091. <date>2002-05-26</date>
  22092. <license uri="http://www.php.net/license">PHP License</license>
  22093. <notes>
  22094. POSIX getopt compatibility fix: treat first element of args
  22095. array as command name
  22096. </notes>
  22097. </release>
  22098. <release>
  22099. <version>
  22100. <release>0.10</release>
  22101. <api>0.10</api>
  22102. </version>
  22103. <stability>
  22104. <release>beta</release>
  22105. <api>beta</api>
  22106. </stability>
  22107. <date>2002-05-12</date>
  22108. <license uri="http://www.php.net/license">PHP License</license>
  22109. <notes>
  22110. Packaging fix
  22111. </notes>
  22112. </release>
  22113. <release>
  22114. <version>
  22115. <release>0.9</release>
  22116. <api>0.9</api>
  22117. </version>
  22118. <stability>
  22119. <release>beta</release>
  22120. <api>beta</api>
  22121. </stability>
  22122. <date>2002-05-12</date>
  22123. <license uri="http://www.php.net/license">PHP License</license>
  22124. <notes>
  22125. Initial release
  22126. </notes>
  22127. </release>
  22128. </changelog>
  22129. </package>
  22130. �����������������������������������������������������������������������������������Console_Getopt-1.4.3/Console/Getopt.php�������������������������������������������������������������0000644�0001750�0001750�00000032503�13565302573�017704� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  22131. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  22132. /**
  22133. * PHP Version 5
  22134. *
  22135. * Copyright (c) 2001-2015, The PEAR developers
  22136. *
  22137. * This source file is subject to the BSD-2-Clause license,
  22138. * that is bundled with this package in the file LICENSE, and is
  22139. * available through the world-wide-web at the following url:
  22140. * http://opensource.org/licenses/bsd-license.php.
  22141. *
  22142. * @category Console
  22143. * @package Console_Getopt
  22144. * @author Andrei Zmievski <andrei@php.net>
  22145. * @license http://opensource.org/licenses/bsd-license.php BSD-2-Clause
  22146. * @version CVS: $Id$
  22147. * @link http://pear.php.net/package/Console_Getopt
  22148. */
  22149. require_once 'PEAR.php';
  22150. /**
  22151. * Command-line options parsing class.
  22152. *
  22153. * @category Console
  22154. * @package Console_Getopt
  22155. * @author Andrei Zmievski <andrei@php.net>
  22156. * @license http://opensource.org/licenses/bsd-license.php BSD-2-Clause
  22157. * @link http://pear.php.net/package/Console_Getopt
  22158. */
  22159. class Console_Getopt
  22160. {
  22161. /**
  22162. * Parses the command-line options.
  22163. *
  22164. * The first parameter to this function should be the list of command-line
  22165. * arguments without the leading reference to the running program.
  22166. *
  22167. * The second parameter is a string of allowed short options. Each of the
  22168. * option letters can be followed by a colon ':' to specify that the option
  22169. * requires an argument, or a double colon '::' to specify that the option
  22170. * takes an optional argument.
  22171. *
  22172. * The third argument is an optional array of allowed long options. The
  22173. * leading '--' should not be included in the option name. Options that
  22174. * require an argument should be followed by '=', and options that take an
  22175. * option argument should be followed by '=='.
  22176. *
  22177. * The return value is an array of two elements: the list of parsed
  22178. * options and the list of non-option command-line arguments. Each entry in
  22179. * the list of parsed options is a pair of elements - the first one
  22180. * specifies the option, and the second one specifies the option argument,
  22181. * if there was one.
  22182. *
  22183. * Long and short options can be mixed.
  22184. *
  22185. * Most of the semantics of this function are based on GNU getopt_long().
  22186. *
  22187. * @param array $args an array of command-line arguments
  22188. * @param string $short_options specifies the list of allowed short options
  22189. * @param array $long_options specifies the list of allowed long options
  22190. * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
  22191. *
  22192. * @return array two-element array containing the list of parsed options and
  22193. * the non-option arguments
  22194. */
  22195. public static function getopt2($args, $short_options, $long_options = null, $skip_unknown = false)
  22196. {
  22197. return Console_Getopt::doGetopt(2, $args, $short_options, $long_options, $skip_unknown);
  22198. }
  22199. /**
  22200. * This function expects $args to start with the script name (POSIX-style).
  22201. * Preserved for backwards compatibility.
  22202. *
  22203. * @param array $args an array of command-line arguments
  22204. * @param string $short_options specifies the list of allowed short options
  22205. * @param array $long_options specifies the list of allowed long options
  22206. *
  22207. * @see getopt2()
  22208. * @return array two-element array containing the list of parsed options and
  22209. * the non-option arguments
  22210. */
  22211. public static function getopt($args, $short_options, $long_options = null, $skip_unknown = false)
  22212. {
  22213. return Console_Getopt::doGetopt(1, $args, $short_options, $long_options, $skip_unknown);
  22214. }
  22215. /**
  22216. * The actual implementation of the argument parsing code.
  22217. *
  22218. * @param int $version Version to use
  22219. * @param array $args an array of command-line arguments
  22220. * @param string $short_options specifies the list of allowed short options
  22221. * @param array $long_options specifies the list of allowed long options
  22222. * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
  22223. *
  22224. * @return array
  22225. */
  22226. public static function doGetopt($version, $args, $short_options, $long_options = null, $skip_unknown = false)
  22227. {
  22228. // in case you pass directly readPHPArgv() as the first arg
  22229. if (PEAR::isError($args)) {
  22230. return $args;
  22231. }
  22232. if (empty($args)) {
  22233. return array(array(), array());
  22234. }
  22235. $non_opts = $opts = array();
  22236. settype($args, 'array');
  22237. if ($long_options) {
  22238. sort($long_options);
  22239. }
  22240. /*
  22241. * Preserve backwards compatibility with callers that relied on
  22242. * erroneous POSIX fix.
  22243. */
  22244. if ($version < 2) {
  22245. if (isset($args[0][0]) && $args[0][0] != '-') {
  22246. array_shift($args);
  22247. }
  22248. }
  22249. for ($i = 0; $i < count($args); $i++) {
  22250. $arg = $args[$i];
  22251. /* The special element '--' means explicit end of
  22252. options. Treat the rest of the arguments as non-options
  22253. and end the loop. */
  22254. if ($arg == '--') {
  22255. $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
  22256. break;
  22257. }
  22258. if ($arg[0] != '-' || (strlen($arg) > 1 && $arg[1] == '-' && !$long_options)) {
  22259. $non_opts = array_merge($non_opts, array_slice($args, $i));
  22260. break;
  22261. } elseif (strlen($arg) > 1 && $arg[1] == '-') {
  22262. $error = Console_Getopt::_parseLongOption(substr($arg, 2),
  22263. $long_options,
  22264. $opts,
  22265. $i,
  22266. $args,
  22267. $skip_unknown);
  22268. if (PEAR::isError($error)) {
  22269. return $error;
  22270. }
  22271. } elseif ($arg == '-') {
  22272. // - is stdin
  22273. $non_opts = array_merge($non_opts, array_slice($args, $i));
  22274. break;
  22275. } else {
  22276. $error = Console_Getopt::_parseShortOption(substr($arg, 1),
  22277. $short_options,
  22278. $opts,
  22279. $i,
  22280. $args,
  22281. $skip_unknown);
  22282. if (PEAR::isError($error)) {
  22283. return $error;
  22284. }
  22285. }
  22286. }
  22287. return array($opts, $non_opts);
  22288. }
  22289. /**
  22290. * Parse short option
  22291. *
  22292. * @param string $arg Argument
  22293. * @param string[] $short_options Available short options
  22294. * @param string[][] &$opts
  22295. * @param int &$argIdx
  22296. * @param string[] $args
  22297. * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
  22298. *
  22299. * @return void
  22300. */
  22301. protected static function _parseShortOption($arg, $short_options, &$opts, &$argIdx, $args, $skip_unknown)
  22302. {
  22303. for ($i = 0; $i < strlen($arg); $i++) {
  22304. $opt = $arg[$i];
  22305. $opt_arg = null;
  22306. /* Try to find the short option in the specifier string. */
  22307. if (($spec = strstr($short_options, $opt)) === false || $arg[$i] == ':') {
  22308. if ($skip_unknown === true) {
  22309. break;
  22310. }
  22311. $msg = "Console_Getopt: unrecognized option -- $opt";
  22312. return PEAR::raiseError($msg);
  22313. }
  22314. if (strlen($spec) > 1 && $spec[1] == ':') {
  22315. if (strlen($spec) > 2 && $spec[2] == ':') {
  22316. if ($i + 1 < strlen($arg)) {
  22317. /* Option takes an optional argument. Use the remainder of
  22318. the arg string if there is anything left. */
  22319. $opts[] = array($opt, substr($arg, $i + 1));
  22320. break;
  22321. }
  22322. } else {
  22323. /* Option requires an argument. Use the remainder of the arg
  22324. string if there is anything left. */
  22325. if ($i + 1 < strlen($arg)) {
  22326. $opts[] = array($opt, substr($arg, $i + 1));
  22327. break;
  22328. } else if (isset($args[++$argIdx])) {
  22329. $opt_arg = $args[$argIdx];
  22330. /* Else use the next argument. */;
  22331. if (Console_Getopt::_isShortOpt($opt_arg)
  22332. || Console_Getopt::_isLongOpt($opt_arg)) {
  22333. $msg = "option requires an argument --$opt";
  22334. return PEAR::raiseError("Console_Getopt: " . $msg);
  22335. }
  22336. } else {
  22337. $msg = "option requires an argument --$opt";
  22338. return PEAR::raiseError("Console_Getopt: " . $msg);
  22339. }
  22340. }
  22341. }
  22342. $opts[] = array($opt, $opt_arg);
  22343. }
  22344. }
  22345. /**
  22346. * Checks if an argument is a short option
  22347. *
  22348. * @param string $arg Argument to check
  22349. *
  22350. * @return bool
  22351. */
  22352. protected static function _isShortOpt($arg)
  22353. {
  22354. return strlen($arg) == 2 && $arg[0] == '-'
  22355. && preg_match('/[a-zA-Z]/', $arg[1]);
  22356. }
  22357. /**
  22358. * Checks if an argument is a long option
  22359. *
  22360. * @param string $arg Argument to check
  22361. *
  22362. * @return bool
  22363. */
  22364. protected static function _isLongOpt($arg)
  22365. {
  22366. return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' &&
  22367. preg_match('/[a-zA-Z]+$/', substr($arg, 2));
  22368. }
  22369. /**
  22370. * Parse long option
  22371. *
  22372. * @param string $arg Argument
  22373. * @param string[] $long_options Available long options
  22374. * @param string[][] &$opts
  22375. * @param int &$argIdx
  22376. * @param string[] $args
  22377. *
  22378. * @return void|PEAR_Error
  22379. */
  22380. protected static function _parseLongOption($arg, $long_options, &$opts, &$argIdx, $args, $skip_unknown)
  22381. {
  22382. @list($opt, $opt_arg) = explode('=', $arg, 2);
  22383. $opt_len = strlen($opt);
  22384. for ($i = 0; $i < count($long_options); $i++) {
  22385. $long_opt = $long_options[$i];
  22386. $opt_start = substr($long_opt, 0, $opt_len);
  22387. $long_opt_name = str_replace('=', '', $long_opt);
  22388. /* Option doesn't match. Go on to the next one. */
  22389. if ($long_opt_name != $opt) {
  22390. continue;
  22391. }
  22392. $opt_rest = substr($long_opt, $opt_len);
  22393. /* Check that the options uniquely matches one of the allowed
  22394. options. */
  22395. if ($i + 1 < count($long_options)) {
  22396. $next_option_rest = substr($long_options[$i + 1], $opt_len);
  22397. } else {
  22398. $next_option_rest = '';
  22399. }
  22400. if ($opt_rest != '' && $opt[0] != '=' &&
  22401. $i + 1 < count($long_options) &&
  22402. $opt == substr($long_options[$i+1], 0, $opt_len) &&
  22403. $next_option_rest != '' &&
  22404. $next_option_rest[0] != '=') {
  22405. $msg = "Console_Getopt: option --$opt is ambiguous";
  22406. return PEAR::raiseError($msg);
  22407. }
  22408. if (substr($long_opt, -1) == '=') {
  22409. if (substr($long_opt, -2) != '==') {
  22410. /* Long option requires an argument.
  22411. Take the next argument if one wasn't specified. */;
  22412. if (!strlen($opt_arg)) {
  22413. if (!isset($args[++$argIdx])) {
  22414. $msg = "Console_Getopt: option requires an argument --$opt";
  22415. return PEAR::raiseError($msg);
  22416. }
  22417. $opt_arg = $args[$argIdx];
  22418. }
  22419. if (Console_Getopt::_isShortOpt($opt_arg)
  22420. || Console_Getopt::_isLongOpt($opt_arg)) {
  22421. $msg = "Console_Getopt: option requires an argument --$opt";
  22422. return PEAR::raiseError($msg);
  22423. }
  22424. }
  22425. } else if ($opt_arg) {
  22426. $msg = "Console_Getopt: option --$opt doesn't allow an argument";
  22427. return PEAR::raiseError($msg);
  22428. }
  22429. $opts[] = array('--' . $opt, $opt_arg);
  22430. return;
  22431. }
  22432. if ($skip_unknown === true) {
  22433. return;
  22434. }
  22435. return PEAR::raiseError("Console_Getopt: unrecognized option --$opt");
  22436. }
  22437. /**
  22438. * Safely read the $argv PHP array across different PHP configurations.
  22439. * Will take care on register_globals and register_argc_argv ini directives
  22440. *
  22441. * @return mixed the $argv PHP array or PEAR error if not registered
  22442. */
  22443. public static function readPHPArgv()
  22444. {
  22445. global $argv;
  22446. if (!is_array($argv)) {
  22447. if (!@is_array($_SERVER['argv'])) {
  22448. if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
  22449. $msg = "Could not read cmd args (register_argc_argv=Off?)";
  22450. return PEAR::raiseError("Console_Getopt: " . $msg);
  22451. }
  22452. return $GLOBALS['HTTP_SERVER_VARS']['argv'];
  22453. }
  22454. return $_SERVER['argv'];
  22455. }
  22456. return $argv;
  22457. }
  22458. }
  22459. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Console_Getopt-1.4.3/tests/001-getopt.phpt����������������������������������������������������������0000644�0001750�0001750�00000002316�13565302573�020165� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������--TEST--
  22460. Console_Getopt
  22461. --FILE--
  22462. <?php
  22463. require_once 'Console/Getopt.php';
  22464. PEAR::setErrorHandling(PEAR_ERROR_PRINT, "%s\n\n");
  22465. function test($argstr, $optstr) {
  22466. $argv = preg_split('/[[:space:]]+/', $argstr);
  22467. if (PEAR::isError($options = Console_Getopt::getopt($argv, $optstr))) {
  22468. return;
  22469. }
  22470. $opts = $options[0];
  22471. $non_opts = $options[1];
  22472. $i = 0;
  22473. print "options: ";
  22474. foreach ($opts as $o => $d) {
  22475. if ($i++ > 0) {
  22476. print ", ";
  22477. }
  22478. print $d[0] . '=' . $d[1];
  22479. }
  22480. print "\n";
  22481. print "params: " . implode(", ", $non_opts) . "\n";
  22482. print "\n";
  22483. }
  22484. test("-abc", "abc");
  22485. test("-abc foo", "abc");
  22486. test("-abc foo", "abc:");
  22487. test("-abc foo bar gazonk", "abc");
  22488. test("-abc foo bar gazonk", "abc:");
  22489. test("-a -b -c", "abc");
  22490. test("-a -b -c", "abc:");
  22491. test("-abc", "ab:c");
  22492. test("-abc foo -bar gazonk", "abc");
  22493. ?>
  22494. --EXPECT--
  22495. options: a=, b=, c=
  22496. params:
  22497. options: a=, b=, c=
  22498. params: foo
  22499. options: a=, b=, c=foo
  22500. params:
  22501. options: a=, b=, c=
  22502. params: foo, bar, gazonk
  22503. options: a=, b=, c=foo
  22504. params: bar, gazonk
  22505. options: a=, b=, c=
  22506. params:
  22507. Console_Getopt: option requires an argument --c
  22508. options: a=, b=c
  22509. params:
  22510. options: a=, b=, c=
  22511. params: foo, -bar, gazonk
  22512. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Console_Getopt-1.4.3/tests/bug10557.phpt������������������������������������������������������������0000644�0001750�0001750�00000000756�13565302573�017552� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������--TEST--
  22513. Console_Getopt [bug 10557]
  22514. --SKIPIF--
  22515. --FILE--
  22516. <?php
  22517. $_SERVER['argv'] =
  22518. $argv = array('hi', '-fjjohnston@mail.com', '--to', '--mailpack', '--debug');
  22519. require_once 'Console/Getopt.php';
  22520. $ret = Console_Getopt::getopt(Console_Getopt::readPHPArgv(), 'f:t:',
  22521. array('from=','to=','mailpack=','direction=','verbose','debug'));
  22522. if(PEAR::isError($ret))
  22523. {
  22524. echo $ret->getMessage()."\n";
  22525. echo 'FATAL';
  22526. exit;
  22527. }
  22528. print_r($ret);
  22529. ?>
  22530. --EXPECT--
  22531. Console_Getopt: option requires an argument --to
  22532. FATAL������������������Console_Getopt-1.4.3/tests/bug11068.phpt������������������������������������������������������������0000644�0001750�0001750�00000001431�13565302573�017537� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������--TEST--
  22533. Console_Getopt [bug 11068]
  22534. --SKIPIF--
  22535. --FILE--
  22536. <?php
  22537. $_SERVER['argv'] =
  22538. $argv = array('hi', '-fjjohnston@mail.com', '--to', 'hi', '-');
  22539. require_once 'Console/Getopt.php';
  22540. $ret = Console_Getopt::getopt(Console_Getopt::readPHPArgv(), 'f:t:',
  22541. array('from=','to=','mailpack=','direction=','verbose','debug'));
  22542. if(PEAR::isError($ret))
  22543. {
  22544. echo $ret->getMessage()."\n";
  22545. echo 'FATAL';
  22546. exit;
  22547. }
  22548. print_r($ret);
  22549. ?>
  22550. --EXPECT--
  22551. Array
  22552. (
  22553. [0] => Array
  22554. (
  22555. [0] => Array
  22556. (
  22557. [0] => f
  22558. [1] => jjohnston@mail.com
  22559. )
  22560. [1] => Array
  22561. (
  22562. [0] => --to
  22563. [1] => hi
  22564. )
  22565. )
  22566. [1] => Array
  22567. (
  22568. [0] => -
  22569. )
  22570. )���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Console_Getopt-1.4.3/tests/bug13140.phpt������������������������������������������������������������0000644�0001750�0001750�00000002505�13565302573�017533� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������--TEST--
  22571. Console_Getopt [bug 13140]
  22572. --SKIPIF--
  22573. --FILE--
  22574. <?php
  22575. $_SERVER['argv'] = $argv =
  22576. array('--bob', '--foo' , '-bar', '--test', '-rq', 'thisshouldbehere');
  22577. require_once 'Console/Getopt.php';
  22578. $cg = new Console_GetOpt();
  22579. print_r($cg->getopt2($cg->readPHPArgv(), 't', array('test'), true));
  22580. print_r($cg->getopt2($cg->readPHPArgv(), 'bar', array('foo'), true));
  22581. ?>
  22582. --EXPECT--
  22583. Array
  22584. (
  22585. [0] => Array
  22586. (
  22587. [0] => Array
  22588. (
  22589. [0] => --test
  22590. [1] =>
  22591. )
  22592. )
  22593. [1] => Array
  22594. (
  22595. [0] => thisshouldbehere
  22596. )
  22597. )
  22598. Array
  22599. (
  22600. [0] => Array
  22601. (
  22602. [0] => Array
  22603. (
  22604. [0] => --foo
  22605. [1] =>
  22606. )
  22607. [1] => Array
  22608. (
  22609. [0] => b
  22610. [1] =>
  22611. )
  22612. [2] => Array
  22613. (
  22614. [0] => a
  22615. [1] =>
  22616. )
  22617. [3] => Array
  22618. (
  22619. [0] => r
  22620. [1] =>
  22621. )
  22622. [4] => Array
  22623. (
  22624. [0] => r
  22625. [1] =>
  22626. )
  22627. )
  22628. [1] => Array
  22629. (
  22630. [0] => thisshouldbehere
  22631. )
  22632. )
  22633. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package.sig�����������������������������������������������������������������������������������������0000644�0001750�0001750�00000000243�13565302626�013034� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������-----BEGIN PGP SIGNATURE-----
  22634. iEYEABECAAYFAl3VhZYACgkQcqMhusJF8XWkbwCeJw8BhTgPII9JdBbLToOrPgEz
  22635. 2jUAoMVHVsygadk15z7oTUbdXMGxTKb7
  22636. =XENS
  22637. -----END PGP SIGNATURE-----
  22638. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package.xml�����������������������������������������������������������������������������������������0000664�0001750�0001750�00000170517�14720722517�013067� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?>
  22639. <package packagerversion="1.10.16" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
  22640. <name>PEAR</name>
  22641. <channel>pear.php.net</channel>
  22642. <summary>PEAR Base System</summary>
  22643. <description>The PEAR package contains:
  22644. * the PEAR installer, for creating, distributing
  22645. and installing packages
  22646. * the PEAR_Exception PHP5 error handling mechanism
  22647. * the PEAR_ErrorStack advanced error handling mechanism
  22648. * the PEAR_Error error handling mechanism
  22649. * the OS_Guess class for retrieving info about the OS
  22650. where PHP is running on
  22651. * the System class for quick handling of common operations
  22652. with files and directories
  22653. * the PEAR base class
  22654. Features in a nutshell:
  22655. * full support for channels
  22656. * pre-download dependency validation
  22657. * new package.xml 2.0 format allows tremendous flexibility while maintaining BC
  22658. * support for optional dependency groups and limited support for sub-packaging
  22659. * robust dependency support
  22660. * full dependency validation on uninstall
  22661. * remote install for hosts with only ftp access - no more problems with
  22662. restricted host installation
  22663. * full support for mirroring
  22664. * support for bundling several packages into a single tarball
  22665. * support for static dependencies on a url-based package
  22666. * support for custom file roles and installation tasks</description>
  22667. <lead>
  22668. <name>Greg Beaver</name>
  22669. <user>cellog</user>
  22670. <email>cellog@php.net</email>
  22671. <active>no</active>
  22672. </lead>
  22673. <lead>
  22674. <name>Pierre-Alain Joye</name>
  22675. <user>pajoye</user>
  22676. <email>pierre@php.net</email>
  22677. <active>no</active>
  22678. </lead>
  22679. <lead>
  22680. <name>Stig Bakken</name>
  22681. <user>ssb</user>
  22682. <email>stig@php.net</email>
  22683. <active>no</active>
  22684. </lead>
  22685. <lead>
  22686. <name>Tomas V.V.Cox</name>
  22687. <user>cox</user>
  22688. <email>cox@idecnet.com</email>
  22689. <active>no</active>
  22690. </lead>
  22691. <lead>
  22692. <name>Helgi Thormar</name>
  22693. <user>dufuz</user>
  22694. <email>dufuz@php.net</email>
  22695. <active>no</active>
  22696. </lead>
  22697. <lead>
  22698. <name>Christian Weiske</name>
  22699. <user>cweiske</user>
  22700. <email>cweiske@php.net</email>
  22701. <active>no</active>
  22702. </lead>
  22703. <lead>
  22704. <name>Chuck Burgess</name>
  22705. <user>ashnazg</user>
  22706. <email>ashnazg@php.net</email>
  22707. <active>yes</active>
  22708. </lead>
  22709. <developer>
  22710. <name>Tias Guns</name>
  22711. <user>tias</user>
  22712. <email>tias@php.net</email>
  22713. <active>no</active>
  22714. </developer>
  22715. <helper>
  22716. <name>Tim Jackson</name>
  22717. <user>timj</user>
  22718. <email>timj@php.net</email>
  22719. <active>no</active>
  22720. </helper>
  22721. <helper>
  22722. <name>Bertrand Gugger</name>
  22723. <user>toggg</user>
  22724. <email>toggg@php.net</email>
  22725. <active>no</active>
  22726. </helper>
  22727. <helper>
  22728. <name>Martin Jansen</name>
  22729. <user>mj</user>
  22730. <email>mj@php.net</email>
  22731. <active>no</active>
  22732. </helper>
  22733. <date>2024-11-24</date>
  22734. <time>22:14:39</time>
  22735. <version>
  22736. <release>1.10.16</release>
  22737. <api>1.10.1</api>
  22738. </version>
  22739. <stability>
  22740. <release>stable</release>
  22741. <api>stable</api>
  22742. </stability>
  22743. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  22744. <notes>
  22745. * PR #141: Fix bug #27796: &quot;Array to string&quot; conversion warnings on installs/other actions
  22746. * PR #145: Never reference E_STRICT on PHP 8.4+
  22747. * PR #147: Fix tests 8.1+
  22748. </notes>
  22749. <contents>
  22750. <dir name="/">
  22751. <file md5sum="c0482b234f269360953c87472b5cf746" name="OS/Guess.php" role="php">
  22752. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22753. </file>
  22754. <file md5sum="3b371e9c6b4d667abb8be01f2788dcf9" name="PEAR/ChannelFile/Parser.php" role="php">
  22755. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22756. </file>
  22757. <file md5sum="8fd87e64002e11fd86eb2f3fbfee6599" name="PEAR/Command/Auth.xml" role="php" />
  22758. <file md5sum="65b6b36c4cc53f2836ad09b070829877" name="PEAR/Command/Auth.php" role="php">
  22759. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22760. </file>
  22761. <file md5sum="ce6bb5b6fdc02e0f50e7676403fd84a4" name="PEAR/Command/Build.xml" role="php" />
  22762. <file md5sum="e19325f59c4013694a4a06b61e7ac3be" name="PEAR/Command/Build.php" role="php">
  22763. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22764. </file>
  22765. <file md5sum="6d5aab4d4308c3005b5f584c7783a031" name="PEAR/Command/Channels.xml" role="php" />
  22766. <file md5sum="56b3b36834a751f6e7b86dcba6cefe2f" name="PEAR/Command/Channels.php" role="php">
  22767. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22768. </file>
  22769. <file md5sum="aae7cc03e9b7fe7c4f504b970dca5411" name="PEAR/Command/Common.php" role="php">
  22770. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22771. </file>
  22772. <file md5sum="91f189cb9423b5e87ee0abc5ea1a2be3" name="PEAR/Command/Config.xml" role="php" />
  22773. <file md5sum="3c5d633b0e8d4e39e9bfb286f51130a5" name="PEAR/Command/Config.php" role="php">
  22774. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22775. </file>
  22776. <file md5sum="24d05213cae7faa3880bbb5e40998867" name="PEAR/Command/Install.xml" role="php" />
  22777. <file md5sum="3dab53d092878709aa16f73143600107" name="PEAR/Command/Install.php" role="php">
  22778. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22779. </file>
  22780. <file md5sum="5cb62a04c0a268f4edd64a49a3895c92" name="PEAR/Command/Mirror.xml" role="php" />
  22781. <file md5sum="2f5798bb62453d8d1bffa3d050584232" name="PEAR/Command/Mirror.php" role="php">
  22782. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22783. </file>
  22784. <file md5sum="9367dcd7e4dbdde423f9c4c7d3f3a919" name="PEAR/Command/Package.xml" role="php" />
  22785. <file md5sum="57521ff1e89bcd23a853134f30836990" name="PEAR/Command/Package.php" role="php">
  22786. <tasks:replace from="@DATA-DIR@" to="data_dir" type="pear-config" />
  22787. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22788. </file>
  22789. <file md5sum="28dc842ea725d8787b9f9c3dbca5aa22" name="PEAR/Command/Pickle.xml" role="php" />
  22790. <file md5sum="4025dd6411a73b19a5c19b3300060625" name="PEAR/Command/Pickle.php" role="php">
  22791. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22792. </file>
  22793. <file md5sum="49b046cfc14747f0365e02e9c3f0e6dc" name="PEAR/Command/Registry.xml" role="php" />
  22794. <file md5sum="9f2ea65794243f0b7287e8883f2ab60e" name="PEAR/Command/Registry.php" role="php">
  22795. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22796. </file>
  22797. <file md5sum="29c02e823879b4e3e291f6b36fb339f1" name="PEAR/Command/Remote.xml" role="php" />
  22798. <file md5sum="42c51cbb21c103fe0288e5c16871f401" name="PEAR/Command/Remote.php" role="php">
  22799. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22800. </file>
  22801. <file md5sum="a50c32015005e0761cc3b04679b29ed0" name="PEAR/Command/Test.xml" role="php" />
  22802. <file md5sum="fbb0098fcda7fa29adaa0714a325caea" name="PEAR/Command/Test.php" role="php">
  22803. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22804. </file>
  22805. <file md5sum="9229a9711a893a18298e473212689ab4" name="PEAR/Downloader/Package.php" role="php">
  22806. <tasks:replace from="@PEAR-VER@" to="version" type="package-info" />
  22807. </file>
  22808. <file md5sum="5379be9492842c3af98241c6e226d285" name="PEAR/Frontend/CLI.php" role="php">
  22809. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22810. </file>
  22811. <file md5sum="adae220f10a82e5f78b8689a57387753" name="PEAR/Installer/Role/Common.php" role="php">
  22812. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22813. </file>
  22814. <file md5sum="d8c62e6275e3aaa7784290912406092c" name="PEAR/Installer/Role/Cfg.xml" role="php" />
  22815. <file md5sum="d89d39dcc1c55fb4464f447ce4f84eed" name="PEAR/Installer/Role/Cfg.php" role="php">
  22816. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22817. </file>
  22818. <file md5sum="89a4a2a286e842d45a98974f40a0565c" name="PEAR/Installer/Role/Data.xml" role="php" />
  22819. <file md5sum="89754e8d153937f3dd3266fd8897d965" name="PEAR/Installer/Role/Data.php" role="php">
  22820. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22821. </file>
  22822. <file md5sum="b1ce0fe105251c3b75209d6518ee69ac" name="PEAR/Installer/Role/Doc.xml" role="php" />
  22823. <file md5sum="849c557b355f78890ef47fcfd12073ab" name="PEAR/Installer/Role/Doc.php" role="php">
  22824. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22825. </file>
  22826. <file md5sum="af71c0ad42d16a323afe24a4f884ef15" name="PEAR/Installer/Role/Ext.xml" role="php" />
  22827. <file md5sum="7d3dc799576b3e3d74d187de118cc3e4" name="PEAR/Installer/Role/Ext.php" role="php">
  22828. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22829. </file>
  22830. <file md5sum="da6743f1e45cce72ea13aef5cdb14867" name="PEAR/Installer/Role/Man.xml" role="php" />
  22831. <file md5sum="2509a027ae42c4b96aa49557325a0ec4" name="PEAR/Installer/Role/Man.php" role="php">
  22832. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22833. </file>
  22834. <file md5sum="ef88f0321d3e481c2130c95122cf76d8" name="PEAR/Installer/Role/Php.xml" role="php" />
  22835. <file md5sum="d7547896ec403dba2eed9825ed86ea64" name="PEAR/Installer/Role/Php.php" role="php">
  22836. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22837. </file>
  22838. <file md5sum="746461dc3b48af6d24094cb0211608f2" name="PEAR/Installer/Role/Script.xml" role="php" />
  22839. <file md5sum="325c60df7f78be127741621561758f46" name="PEAR/Installer/Role/Script.php" role="php">
  22840. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22841. </file>
  22842. <file md5sum="e147d63f168ea156fc2be38caaa63804" name="PEAR/Installer/Role/Src.xml" role="php" />
  22843. <file md5sum="464722cee0665ebc75757f76532f0ab2" name="PEAR/Installer/Role/Src.php" role="php">
  22844. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22845. </file>
  22846. <file md5sum="a24b596ec987aa5688fc19e8ed4e97ea" name="PEAR/Installer/Role/Test.xml" role="php" />
  22847. <file md5sum="a04eb2f4881ec6e6a13ae78f6a43e3cd" name="PEAR/Installer/Role/Test.php" role="php">
  22848. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22849. </file>
  22850. <file md5sum="7641e71c5785bb33a4261ebe25ed0fd7" name="PEAR/Installer/Role/Www.xml" role="php" />
  22851. <file md5sum="62699034e3186c2d68a6bb56a6cc23eb" name="PEAR/Installer/Role/Www.php" role="php">
  22852. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22853. </file>
  22854. <file md5sum="e95f2d850fce183e221912b2c2056d04" name="PEAR/Installer/Role.php" role="php">
  22855. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22856. </file>
  22857. <file md5sum="cfa08685115d5e2d7635225d73a91f39" name="PEAR/PackageFile/Generator/v1.php" role="php">
  22858. <tasks:replace from="@PEAR-VER@" to="version" type="package-info" />
  22859. </file>
  22860. <file md5sum="8d264031153993866828e82129e389ab" name="PEAR/PackageFile/Generator/v2.php" role="php">
  22861. <tasks:replace from="@PEAR-VER@" to="version" type="package-info" />
  22862. </file>
  22863. <file md5sum="a2206e0e32ad2ba2f4e2a0c1797f295c" name="PEAR/PackageFile/Parser/v1.php" role="php">
  22864. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22865. </file>
  22866. <file md5sum="3857f4b60878d64551a7cdae783437f7" name="PEAR/PackageFile/Parser/v2.php" role="php">
  22867. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22868. </file>
  22869. <file md5sum="a253999b2109460badc3c71ad2c069e7" name="PEAR/PackageFile/v2/rw.php" role="php">
  22870. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22871. </file>
  22872. <file md5sum="44dafa313204f68b5fb54b9f61a86d0c" name="PEAR/PackageFile/v2/Validator.php" role="php">
  22873. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22874. </file>
  22875. <file md5sum="cc1ea307bb79c0cbcff4a90a4656e96e" name="PEAR/PackageFile/v1.php" role="php">
  22876. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22877. </file>
  22878. <file md5sum="9e6012d4ef9cb8e12e2ccadcbdcf3d24" name="PEAR/PackageFile/v2.php" role="php">
  22879. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22880. </file>
  22881. <file md5sum="ce77cc6593d8d4eda5ff8620ff09d6ec" name="PEAR/REST/10.php" role="php">
  22882. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22883. </file>
  22884. <file md5sum="bdbd1f2e8afa2cddddecf2687579d316" name="PEAR/REST/11.php" role="php">
  22885. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22886. </file>
  22887. <file md5sum="11179085b6efb577d37f45cc25e42a35" name="PEAR/REST/13.php" role="php">
  22888. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22889. </file>
  22890. <file md5sum="0eb57da4993a3cfed69fe8135f10b05a" name="PEAR/Task/Postinstallscript/rw.php" role="php">
  22891. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22892. </file>
  22893. <file md5sum="46c1bdb9a7af8628643e639ec1c3bc58" name="PEAR/Task/Replace/rw.php" role="php">
  22894. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22895. </file>
  22896. <file md5sum="50e20dbde7e51bb5e11545c56482b68a" name="PEAR/Task/Unixeol/rw.php" role="php">
  22897. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22898. </file>
  22899. <file md5sum="340be01552844adb50f6e0fa37aa3ffe" name="PEAR/Task/Windowseol/rw.php" role="php">
  22900. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22901. </file>
  22902. <file md5sum="ec7f8db335ad3b5a25d78682745185d1" name="PEAR/Task/Common.php" role="php">
  22903. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22904. </file>
  22905. <file md5sum="86f240c600447fdb86bd38bd32c66c02" name="PEAR/Task/Postinstallscript.php" role="php">
  22906. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22907. </file>
  22908. <file md5sum="c993cdedc64084a1c4b735baec048d50" name="PEAR/Task/Replace.php" role="php">
  22909. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22910. </file>
  22911. <file md5sum="76c4ca15858c005cda208a9da1f5eb2d" name="PEAR/Task/Unixeol.php" role="php">
  22912. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22913. </file>
  22914. <file md5sum="d790eb3aa5ba22a33e42cb7c5f99fb25" name="PEAR/Task/Windowseol.php" role="php">
  22915. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22916. </file>
  22917. <file md5sum="b3c83ca826a4825ca2db726ed7d4b245" name="PEAR/Validator/PECL.php" role="php">
  22918. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22919. </file>
  22920. <file md5sum="7d89f25a54d100c2afd562fb3bd7c308" name="PEAR/Builder.php" role="php">
  22921. <tasks:replace from="@PEAR-VER@" to="version" type="package-info" />
  22922. </file>
  22923. <file md5sum="edcd484ecfd400875df90ee480c5384d" name="PEAR/ChannelFile.php" role="php">
  22924. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22925. </file>
  22926. <file md5sum="a5d56f9d15ff6d22c12f7db851f281e8" name="PEAR/Command.php" role="php">
  22927. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22928. </file>
  22929. <file md5sum="0af44a13df74291ac2f46949d2035e0e" name="PEAR/Common.php" role="php">
  22930. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22931. </file>
  22932. <file md5sum="64d0f7b2737e5f182d0ab98bc77930d2" name="PEAR/Config.php" role="php">
  22933. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22934. </file>
  22935. <file md5sum="7e8dfae09802be7f2e6170062bb80cbd" name="PEAR/DependencyDB.php" role="php">
  22936. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22937. </file>
  22938. <file md5sum="76fdabdee883fd16b986332552b9e3dc" name="PEAR/Dependency2.php" role="php">
  22939. <tasks:replace from="@PEAR-VER@" to="version" type="package-info" />
  22940. </file>
  22941. <file md5sum="2ccb7bb2d00eae201ee7ae1eef49ad8a" name="PEAR/Downloader.php" role="php">
  22942. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22943. </file>
  22944. <file md5sum="ea807632b63d2e0acd6924db23aaa0eb" name="PEAR/ErrorStack.php" role="php">
  22945. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22946. </file>
  22947. <file md5sum="544ed48cab9407a936d058d09e773e22" name="PEAR/Exception.php" role="php">
  22948. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22949. </file>
  22950. <file md5sum="31c0b91767550adf77f1c9bea92a0559" name="PEAR/Frontend.php" role="php">
  22951. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22952. </file>
  22953. <file md5sum="b8b85e42b840676ad3ae1c15e55b005b" name="PEAR/Installer.php" role="php">
  22954. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22955. </file>
  22956. <file md5sum="2929765413405abf63574c8a544e4a04" name="PEAR/PackageFile.php" role="php">
  22957. <tasks:replace from="@PEAR-VER@" to="version" type="package-info" />
  22958. </file>
  22959. <file md5sum="ad25a29d464b62cbaaa3ca96e622526c" name="PEAR/Packager.php" role="php">
  22960. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22961. </file>
  22962. <file md5sum="ef9d00adaeccff7516f08170096026d3" name="PEAR/Proxy.php" role="php">
  22963. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22964. </file>
  22965. <file md5sum="ae8c1dcfddb6a2717e09239bb1430dc7" name="PEAR/Registry.php" role="php">
  22966. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22967. </file>
  22968. <file md5sum="389d1bad72267d3ed770abf0662d8086" name="PEAR/REST.php" role="php">
  22969. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22970. </file>
  22971. <file md5sum="df0d7022e22fd78ecd82080ff4f108d5" name="PEAR/RunTest.php" role="php">
  22972. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22973. </file>
  22974. <file md5sum="3711c281e0234203ec7879f53bc766ab" name="PEAR/Validate.php" role="php">
  22975. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22976. </file>
  22977. <file md5sum="da9510087eddd489945dde07260256ee" name="PEAR/XMLParser.php" role="php">
  22978. <tasks:replace from="@package_version@" to="version" type="package-info" />
  22979. </file>
  22980. <file baseinstalldir="/" md5sum="d888d06143e3cac0dae78bbb2e761366" name="scripts/pear.bat" role="script">
  22981. <tasks:replace from="@bin_dir@" to="bin_dir" type="pear-config" />
  22982. <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
  22983. <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
  22984. <tasks:windowseol />
  22985. </file>
  22986. <file baseinstalldir="/" md5sum="17d7d08ce2c6c476eeaae4763f69efcc" name="scripts/peardev.bat" role="script">
  22987. <tasks:replace from="@bin_dir@" to="bin_dir" type="pear-config" />
  22988. <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
  22989. <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
  22990. <tasks:windowseol />
  22991. </file>
  22992. <file baseinstalldir="/" md5sum="1c5819d67da59739e6298d6094c58f7b" name="scripts/pecl.bat" role="script">
  22993. <tasks:replace from="@bin_dir@" to="bin_dir" type="pear-config" />
  22994. <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
  22995. <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
  22996. <tasks:windowseol />
  22997. </file>
  22998. <file baseinstalldir="/" md5sum="8ac139504e80bede470aef6d405100b6" name="scripts/pear.sh" role="script">
  22999. <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
  23000. <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" />
  23001. <tasks:replace from="@pear_version@" to="version" type="package-info" />
  23002. <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
  23003. <tasks:unixeol />
  23004. </file>
  23005. <file baseinstalldir="/" md5sum="08ea03525b4ba914dfd9ec69c4238cf4" name="scripts/peardev.sh" role="script">
  23006. <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
  23007. <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" />
  23008. <tasks:replace from="@pear_version@" to="version" type="package-info" />
  23009. <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
  23010. <tasks:unixeol />
  23011. </file>
  23012. <file baseinstalldir="/" md5sum="bde09b17fa816d58bb136375a13119c3" name="scripts/pecl.sh" role="script">
  23013. <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
  23014. <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" />
  23015. <tasks:replace from="@pear_version@" to="version" type="package-info" />
  23016. <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
  23017. <tasks:unixeol />
  23018. </file>
  23019. <file baseinstalldir="/" md5sum="9b5d5e5bd017c50df00c6b34ef32652e" name="scripts/pearcmd.php" role="php">
  23020. <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
  23021. <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" />
  23022. <tasks:replace from="@pear_version@" to="version" type="package-info" />
  23023. <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
  23024. </file>
  23025. <file baseinstalldir="/" md5sum="48867dfbb41f2532d034f56a79565893" name="scripts/peclcmd.php" role="php">
  23026. <tasks:replace from="@php_bin@" to="php_bin" type="pear-config" />
  23027. <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" />
  23028. <tasks:replace from="@pear_version@" to="version" type="package-info" />
  23029. <tasks:replace from="@include_path@" to="php_dir" type="pear-config" />
  23030. </file>
  23031. <file md5sum="45b44486d8090de17b2a8b4211fab247" name="LICENSE" role="doc" />
  23032. <file md5sum="eaac3d33068c6e67573ed44155b149ae" name="INSTALL" role="doc" />
  23033. <file md5sum="69341ea97af9c88956568f8e7e41d4c6" name="package.dtd" role="data" />
  23034. <file md5sum="47aeb7eaff6438beb60bc42bc0e6c658" name="PEAR.php" role="php">
  23035. <tasks:replace from="@package_version@" to="version" type="package-info" />
  23036. </file>
  23037. <file md5sum="cd10521cc4054923a3d2b6e15b4df493" name="README.rst" role="doc" />
  23038. <file md5sum="ba9e5c5a567e51b440808a8ed53cd76d" name="System.php" role="php">
  23039. <tasks:replace from="@package_version@" to="version" type="package-info" />
  23040. </file>
  23041. <file md5sum="acd010e3bc43c0f72df584acde7b9158" name="template.spec" role="data" />
  23042. </dir>
  23043. </contents>
  23044. <dependencies>
  23045. <required>
  23046. <php>
  23047. <min>5.4.0</min>
  23048. </php>
  23049. <pearinstaller>
  23050. <min>1.10.1</min>
  23051. </pearinstaller>
  23052. <package>
  23053. <name>Archive_Tar</name>
  23054. <channel>pear.php.net</channel>
  23055. <min>1.4.9</min>
  23056. <recommended>1.4.4</recommended>
  23057. </package>
  23058. <package>
  23059. <name>Structures_Graph</name>
  23060. <channel>pear.php.net</channel>
  23061. <min>1.1.0</min>
  23062. <recommended>1.1.1</recommended>
  23063. </package>
  23064. <package>
  23065. <name>Console_Getopt</name>
  23066. <channel>pear.php.net</channel>
  23067. <min>1.4.1</min>
  23068. <recommended>1.4.3</recommended>
  23069. </package>
  23070. <package>
  23071. <name>XML_Util</name>
  23072. <channel>pear.php.net</channel>
  23073. <min>1.4.0</min>
  23074. <recommended>1.4.5</recommended>
  23075. </package>
  23076. <extension>
  23077. <name>xml</name>
  23078. </extension>
  23079. <extension>
  23080. <name>pcre</name>
  23081. </extension>
  23082. </required>
  23083. <group hint="PEAR&apos;s web-based installer" name="webinstaller">
  23084. <package>
  23085. <name>PEAR_Frontend_Web</name>
  23086. <channel>pear.php.net</channel>
  23087. <min>0.5.1</min>
  23088. </package>
  23089. </group>
  23090. <group hint="PEAR&apos;s PHP-GTK-based installer" name="gtkinstaller">
  23091. <package>
  23092. <name>PEAR_Frontend_Gtk</name>
  23093. <channel>pear.php.net</channel>
  23094. <min>0.4.0</min>
  23095. </package>
  23096. </group>
  23097. <group hint="PEAR&apos;s PHP-GTK2-based installer" name="gtk2installer">
  23098. <package>
  23099. <name>PEAR_Frontend_Gtk2</name>
  23100. <channel>pear.php.net</channel>
  23101. </package>
  23102. </group>
  23103. </dependencies>
  23104. <phprelease>
  23105. <installconditions>
  23106. <os>
  23107. <name>windows</name>
  23108. </os>
  23109. </installconditions>
  23110. <filelist>
  23111. <install as="pear.bat" name="scripts/pear.bat" />
  23112. <install as="peardev.bat" name="scripts/peardev.bat" />
  23113. <install as="pecl.bat" name="scripts/pecl.bat" />
  23114. <install as="pearcmd.php" name="scripts/pearcmd.php" />
  23115. <install as="peclcmd.php" name="scripts/peclcmd.php" />
  23116. <ignore name="scripts/peardev.sh" />
  23117. <ignore name="scripts/pear.sh" />
  23118. <ignore name="scripts/pecl.sh" />
  23119. </filelist>
  23120. </phprelease>
  23121. <phprelease>
  23122. <filelist>
  23123. <install as="pear" name="scripts/pear.sh" />
  23124. <install as="peardev" name="scripts/peardev.sh" />
  23125. <install as="pecl" name="scripts/pecl.sh" />
  23126. <install as="pearcmd.php" name="scripts/pearcmd.php" />
  23127. <install as="peclcmd.php" name="scripts/peclcmd.php" />
  23128. <ignore name="scripts/pear.bat" />
  23129. <ignore name="scripts/peardev.bat" />
  23130. <ignore name="scripts/pecl.bat" />
  23131. </filelist>
  23132. </phprelease>
  23133. <changelog>
  23134. <release>
  23135. <version>
  23136. <release>1.8.0alpha1</release>
  23137. <api>1.8.0</api>
  23138. </version>
  23139. <stability>
  23140. <release>alpha</release>
  23141. <api>stable</api>
  23142. </stability>
  23143. <date>2009-03-09</date>
  23144. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23145. <notes>
  23146. * Implement Request #10373: if pref_state=stable and installed package=beta, allow up to latest beta version [dufuz]
  23147. * Implement Request #10581: login / logout should map to channel-login / channel-logout [dufuz]
  23148. * Implement Request #10825: Only display the &quot;invalid or missing package file&quot;-error if it makes sense [dufuz]
  23149. * Implement Request #11170: script to generate Command/[command].xml [dufuz]
  23150. * Implement Request #11176: improve channel ... has updated its protocols message [dufuz]
  23151. * Implement Request #12706: pear list -a hard to read [dufuz]
  23152. * Implement Request #11353: upgrade-all and upgrade commands to upgrade within the same stability level [dufuz]
  23153. * Implement Request #13015: Add https discovery for channel.xml [dufuz / initial patch by Martin Roos]
  23154. * Implement Request #13927: install-pear.php should have option to set www_dir [timj]
  23155. * Implement Request #14324: Make the pear install command behave similar to apt-get [dufuz]
  23156. * Implement Request #14325: make pear upgrade with no params behave like pear upgrade-all [dufuz]
  23157. - upgrade-all can be considered deprecated in favor of calling upgrade with no parameters to replicate
  23158. better what other package managers are doing. upgrade-all will still work as intended.
  23159. * Implement Request #14504: add a channel parameter support to the upgrade function [dufuz]
  23160. - Options -c ezc and --channel=ezc got added to upgrade and upgrade-all to allow for
  23161. channel specific upgrades
  23162. * Implement Request #14556: install-pear-nozlib.phar should get download_dir config and other options [cweiske]
  23163. * Implement Request #15566: Add doc.php.net as a default channel [dufuz / saltybeagle]
  23164. * Fix PHP Bug #43857: --program-suffix not always reflected everywhere [cellog]
  23165. * Fix PHP Bug #47323: strotime warnings in make install [dufuz]
  23166. * Fix Bug #13908: pear info command and maintainers inactive not mentioned [dufuz]
  23167. * Fix Bug #13926: install-pear.php does not set cfg_dir if -d option set with no -c option [timj]
  23168. * Fix Bug #13943: tests fail when php.exe path contains spaces [dufuz / jorrit]
  23169. * Fix Bug #13953: config-set/config-show with channel alias fail [cellog]
  23170. * Fix Bug #13958: When a phpt tests exit() or die() xdebug coverage is not generated, patch by izi (David Jean Louis) [izi / dufuz]
  23171. * Fix Bug #14041: Unpredictable unit test processing sequence [dufuz]
  23172. * Fix Bug #14140: Strict warning not suppressed in the shutdown function [dufuz]
  23173. * Fix Bug #14210: pear list -ia brings warnings [dufuz]
  23174. * Fix Bug #14274: PEAR packager mangles package.xml encoding, then complains about it [dufuz]
  23175. * Fix Bug #14287: cannot upgrade from stable to beta via -beta when config is set to stable [dufuz]
  23176. * Fix Bug #14300: Package files themselves can not be served over https [dufuz / initial patch by Martin Roos]
  23177. * Fix Bug #14437: openbasedir warning when loading config [dufuz]
  23178. * Fix Bug #14558: PackageFile.php creates tmp directory outside configured temp_dir [cweiske]
  23179. * Fix Bug #14947: downloadHttp() is missing Host part of the HTTP Request when using Proxy [ifeghali]
  23180. * Fix Bug #14977: PEAR/Frontend.php doesn&apos;t require_once PEAR.php [dufuz]
  23181. * Fix Bug #15750: Unreachable code in PEAR_Downloader [dufuz]
  23182. * Fix Bug #15979: Package files incorrectly removed when splitting a package into multiple pkgs [dufuz]
  23183. * Fix Bug #15914: pear upgrade installs different version if desired version not found [dufuz]
  23184. NOTE!
  23185. Functions that have been deprecated for 3+ years in PEAR_Common, please take a moment
  23186. to migrate over to one of the alternatives that have ben provided:
  23187. * PEAR_Common-&gt;downloadHttp (use PEAR_Downloader-&gt;downloadHttp instead)
  23188. * PEAR_Common-&gt;infoFromTgzFile (use PEAR_PackageFile-&gt;fromTgzFile instead)
  23189. * PEAR_Common-&gt;infoFromDescriptionFile (use PEAR_PackageFile-&gt;fromPackageFile instead)
  23190. * PEAR_Common-&gt;infoFromString (use PEAR_PackageFile-&gt;fromXmlstring instead)
  23191. * PEAR_Common-&gt;infoFromArray (use PEAR_PackageFile-&gt;fromAnyFile instead)
  23192. * PEAR_Common-&gt;xmlFromInfo (use a PEAR_PackageFile_v* object&apos;s generator instead)
  23193. * PEAR_Common-&gt;validatePackageInfo (use the validation of PEAR_PackageFile objects)
  23194. * PEAR_Common-&gt;analyzeSourceCode (use a PEAR_PackageFile_v* object instead)
  23195. * PEAR_Common-&gt;detectDependencies (use PEAR_Downloader_Package-&gt;detectDependencies instead)
  23196. * PEAR_Common-&gt;buildProvidesArray (use PEAR_PackageFile_v1-&gt;_buildProvidesArray or
  23197. PEAR_PackageFile_v2_Validator-&gt;_buildProvidesArray)
  23198. PHP 4.4 and 5.1.6 are now the minimum PHP requirements, for brave souls
  23199. pear upgrade -f PEAR will allow people with lower versions
  23200. to upgrade to this release but no guarantees will be made that it will work properly.
  23201. Support for XML RPC channels has been dropped - The only ones that used it
  23202. (pear.php.net and pecl.php.net) have used the REST interface for years now.
  23203. SOAP support also removed as it was only proof of concept.
  23204. Move codebase from the PHP License to New BSD 2 clause license
  23205. </notes>
  23206. </release>
  23207. <release>
  23208. <date>2009-03-27</date>
  23209. <version>
  23210. <release>1.8.0RC1</release>
  23211. <api>1.8.0</api>
  23212. </version>
  23213. <stability>
  23214. <release>beta</release>
  23215. <api>stable</api>
  23216. </stability>
  23217. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23218. <notes>
  23219. * Fix Bug #14331: pear cvstag only works from inside the package directory [dufuz]
  23220. * Fix Bug #16045: E_Notice: Undefined index: channel in PEAR/DependencyDB.php [dufuz]
  23221. * Implemented Request #11230: better error message when mirror not in channel.xml file [dufuz]
  23222. * Implemented Request #13150: Add support for following HTTP 302 redirects [dufuz]
  23223. </notes>
  23224. </release>
  23225. <release>
  23226. <date>2009-04-10</date>
  23227. <version>
  23228. <release>1.8.0</release>
  23229. <api>1.8.0</api>
  23230. </version>
  23231. <stability>
  23232. <release>stable</release>
  23233. <api>stable</api>
  23234. </stability>
  23235. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23236. <notes>
  23237. Changes since RC1:
  23238. * Fix Bug #14792: Bad md5sum for files with replaced content [dufuz]
  23239. * Fix Bug #16057:-r is limited to 4 directories in depth [dufuz]
  23240. * Fix Bug #16077: PEAR5::getStaticProperty does not return a reference to the property [dufuz]
  23241. Remove custom XML_Util class in favor of using upstream XML_Util package as dependency
  23242. RC1 Release Notes:
  23243. * Fix Bug #14331: pear cvstag only works from inside the package directory [dufuz]
  23244. * Fix Bug #16045: E_Notice: Undefined index: channel in PEAR/DependencyDB.php [dufuz]
  23245. * Implemented Request #11230: better error message when mirror not in channel.xml file [dufuz]
  23246. * Implemented Request #13150: Add support for following HTTP 302 redirects [dufuz]
  23247. Alpha1 Release Notes:
  23248. * Implement Request #10373: if pref_state=stable and installed package=beta, allow up to latest beta version [dufuz]
  23249. * Implement Request #10581: login / logout should map to channel-login / channel-logout [dufuz]
  23250. * Implement Request #10825: Only display the &quot;invalid or missing package file&quot;-error if it makes sense [dufuz]
  23251. * Implement Request #11170: script to generate Command/[command].xml [dufuz]
  23252. * Implement Request #11176: improve channel ... has updated its protocols message [dufuz]
  23253. * Implement Request #12706: pear list -a hard to read [dufuz]
  23254. * Implement Request #11353: upgrade-all and upgrade commands to upgrade within the same stability level [dufuz]
  23255. * Implement Request #13015: Add https discovery for channel.xml [dufuz / initial patch by Martin Roos]
  23256. * Implement Request #13927: install-pear.php should have option to set www_dir [timj]
  23257. * Implement Request #14324: Make the pear install command behave similar to apt-get [dufuz]
  23258. * Implement Request #14325: make pear upgrade with no params behave like pear upgrade-all [dufuz]
  23259. - upgrade-all can be considered deprecated in favor of calling upgrade with no parameters to replicate
  23260. better what other package managers are doing. upgrade-all will still work as intended.
  23261. * Implement Request #14504: add a channel parameter support to the upgrade function [dufuz]
  23262. - Options -c ezc and --channel=ezc got added to upgrade and upgrade-all to allow for
  23263. channel specific upgrades
  23264. * Implement Request #14556: install-pear-nozlib.phar should get download_dir config and other options [cweiske]
  23265. * Implement Request #15566: Add doc.php.net as a default channel [dufuz / saltybeagle]
  23266. * Fix PHP Bug #43857: --program-suffix not always reflected everywhere [cellog]
  23267. * Fix PHP Bug #47323: strotime warnings in make install [dufuz]
  23268. * Fix Bug #13908: pear info command and maintainers inactive not mentioned [dufuz]
  23269. * Fix Bug #13926: install-pear.php does not set cfg_dir if -d option set with no -c option [timj]
  23270. * Fix Bug #13943: tests fail when php.exe path contains spaces [dufuz / jorrit]
  23271. * Fix Bug #13953: config-set/config-show with channel alias fail [cellog]
  23272. * Fix Bug #13958: When a phpt tests exit() or die() xdebug coverage is not generated, patch by izi (David Jean Louis) [izi / dufuz]
  23273. * Fix Bug #14041: Unpredictable unit test processing sequence [dufuz]
  23274. * Fix Bug #14140: Strict warning not suppressed in the shutdown function [dufuz]
  23275. * Fix Bug #14210: pear list -ia brings warnings [dufuz]
  23276. * Fix Bug #14274: PEAR packager mangles package.xml encoding, then complains about it [dufuz]
  23277. * Fix Bug #14287: cannot upgrade from stable to beta via -beta when config is set to stable [dufuz]
  23278. * Fix Bug #14300: Package files themselves can not be served over https [dufuz / initial patch by Martin Roos]
  23279. * Fix Bug #14437: openbasedir warning when loading config [dufuz]
  23280. * Fix Bug #14558: PackageFile.php creates tmp directory outside configured temp_dir [cweiske]
  23281. * Fix Bug #14947: downloadHttp() is missing Host part of the HTTP Request when using Proxy [ifeghali]
  23282. * Fix Bug #14977: PEAR/Frontend.php doesn&apos;t require_once PEAR.php [dufuz]
  23283. * Fix Bug #15750: Unreachable code in PEAR_Downloader [dufuz]
  23284. * Fix Bug #15979: Package files incorrectly removed when splitting a package into multiple pkgs [dufuz]
  23285. * Fix Bug #15914: pear upgrade installs different version if desired version not found [dufuz]
  23286. NOTE!
  23287. Functions that have been deprecated for 3+ years in PEAR_Common, please take a moment
  23288. to migrate over to one of the alternatives that have ben provided:
  23289. * PEAR_Common-&gt;downloadHttp (use PEAR_Downloader-&gt;downloadHttp instead)
  23290. * PEAR_Common-&gt;infoFromTgzFile (use PEAR_PackageFile-&gt;fromTgzFile instead)
  23291. * PEAR_Common-&gt;infoFromDescriptionFile (use PEAR_PackageFile-&gt;fromPackageFile instead)
  23292. * PEAR_Common-&gt;infoFromString (use PEAR_PackageFile-&gt;fromXmlstring instead)
  23293. * PEAR_Common-&gt;infoFromArray (use PEAR_PackageFile-&gt;fromAnyFile instead)
  23294. * PEAR_Common-&gt;xmlFromInfo (use a PEAR_PackageFile_v* object&apos;s generator instead)
  23295. * PEAR_Common-&gt;validatePackageInfo (use the validation of PEAR_PackageFile objects)
  23296. * PEAR_Common-&gt;analyzeSourceCode (use a PEAR_PackageFile_v* object instead)
  23297. * PEAR_Common-&gt;detectDependencies (use PEAR_Downloader_Package-&gt;detectDependencies instead)
  23298. * PEAR_Common-&gt;buildProvidesArray (use PEAR_PackageFile_v1-&gt;_buildProvidesArray or
  23299. PEAR_PackageFile_v2_Validator-&gt;_buildProvidesArray)
  23300. PHP 4.4 and 5.1.6 are now the minimum PHP requirements, for brave souls
  23301. pear upgrade -f PEAR will allow people with lower versions
  23302. to upgrade to this release but no guarantees will be made that it will work properly.
  23303. Support for XML RPC channels has been dropped - The only ones that used it
  23304. (pear.php.net and pecl.php.net) have used the REST interface for years now.
  23305. SOAP support also removed as it was only proof of concept.
  23306. Move codebase from the PHP License to New BSD 2 clause license
  23307. </notes>
  23308. </release>
  23309. <release>
  23310. <date>2009-04-15</date>
  23311. <version>
  23312. <release>1.8.1</release>
  23313. <api>1.8.1</api>
  23314. </version>
  23315. <stability>
  23316. <release>stable</release>
  23317. <api>stable</api>
  23318. </stability>
  23319. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23320. <notes>
  23321. * Fix Bug #16099 PEAR crash on PHP4 (parse error) [dufuz]
  23322. </notes>
  23323. </release>
  23324. <release>
  23325. <date>2009-08-18</date>
  23326. <version>
  23327. <release>1.9.0RC1</release>
  23328. <api>1.9.0RC1</api>
  23329. </version>
  23330. <stability>
  23331. <release>beta</release>
  23332. <api>stable</api>
  23333. </stability>
  23334. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23335. <notes>
  23336. * Implement Request #16213: add alias to list-channels output [dufuz]
  23337. * Implement Request #16378: pear svntag [dufuz]
  23338. * Implement Request #16386: PEAR_Config::remove() does not support specifying a channel [timj]
  23339. * Implement Request #16396: package-dependencies should allow package names [dufuz]
  23340. * Fix Bug #11181: pear requests channel.xml from main server instead from mirror [dufuz]
  23341. * Fix Bug #14493: pear install --offline doesn&apos;t print out errors [dufuz]
  23342. * Fix Bug #11348: pear package-dependencies isn&apos;t well explained [dufuz]
  23343. * Fix Bug #16108: PEAR_PackageFile_Generator_v2 PHP4 parse error when running upgrade-all [dufuz]
  23344. * Fix Bug #16113: Installing certain packages fails due incorrect encoding handling [dufuz]
  23345. * Fix Bug #16122: PEAR RunTest failed to run as expected [dufuz]
  23346. * Fix Bug #16366: compiling 5.2.10 leads to non-functioning pear [dufuz]
  23347. * Fix Bug #16387: channel-logout does not support logging out from a non-default channel [timj]
  23348. * Fix Bug #16444: Setting preferred mirror fails [dufuz]
  23349. * Fix the shutdown functions where a index might not exist and thus raise a notice [derick]
  23350. </notes>
  23351. </release>
  23352. <release>
  23353. <date>2009-08-20</date>
  23354. <version>
  23355. <release>1.9.0RC2</release>
  23356. <api>1.9.0RC2</api>
  23357. </version>
  23358. <stability>
  23359. <release>beta</release>
  23360. <api>stable</api>
  23361. </stability>
  23362. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23363. <notes>
  23364. * REST 1.4 file was occasionally being included but REST 1.4 is not intended for this release cycle [dufuz]
  23365. </notes>
  23366. </release>
  23367. <release>
  23368. <date>2009-08-21</date>
  23369. <version>
  23370. <release>1.9.0RC3</release>
  23371. <api>1.9.0RC3</api>
  23372. </version>
  23373. <stability>
  23374. <release>beta</release>
  23375. <api>stable</api>
  23376. </stability>
  23377. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23378. <notes>
  23379. * Improved svntag support to handle packages like PEAR it self [dufuz]
  23380. </notes>
  23381. </release>
  23382. <release>
  23383. <date>2009-08-23</date>
  23384. <version>
  23385. <release>1.9.0RC4</release>
  23386. <api>1.9.0RC4</api>
  23387. </version>
  23388. <stability>
  23389. <release>beta</release>
  23390. <api>stable</api>
  23391. </stability>
  23392. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23393. <notes>
  23394. * Fixed a problem where the original channel could not be set as a preferred_mirror again [dufuz]
  23395. * Make sure channel aliases can&apos;t be made to start with - [dufuz]
  23396. * Output issues with pear search [dufuz]
  23397. * Fixed couple of stray notices [dufuz]
  23398. </notes>
  23399. </release>
  23400. <release>
  23401. <date>2009-09-03</date>
  23402. <version>
  23403. <release>1.9.0</release>
  23404. <api>1.9.0</api>
  23405. </version>
  23406. <stability>
  23407. <release>stable</release>
  23408. <api>stable</api>
  23409. </stability>
  23410. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23411. <notes>
  23412. * Fix Bug #16547: The phar for PEAR installer uses ereg() which is deprecated [dufuz]
  23413. </notes>
  23414. </release>
  23415. <release>
  23416. <date>2010-05-26</date>
  23417. <version>
  23418. <release>1.9.1</release>
  23419. <api>1.9.1</api>
  23420. </version>
  23421. <stability>
  23422. <release>stable</release>
  23423. <api>stable</api>
  23424. </stability>
  23425. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23426. <notes>
  23427. * svntag improvements, tag package files passed into the command and better directory checks [dufuz]
  23428. * rely on Structures_Graph minimum version instead of recommended version [saltybeagle]
  23429. * Fix Bug #12613: running go-pear.phar from C:\ fails [dufuz]
  23430. * Fix Bug #14841: Installing pear into directory with space fails [dufuz]
  23431. * Fix Bug #16644: pear.bat returns syntax error when parenthesis are in install path. [dufuz] [patch by bwaters (Bryan Waters)]
  23432. * Fix Bug #16767: Use of Depreciated HTML Attributes in the Exception class [dufuz] [patch by fuhrysteve (Stephen J. Fuhry)]
  23433. * Fix Bug #16864: &quot;pear list-upgrades -i&quot; issues E_WARNINGS [dufuz] [patch by rquadling (Richard Quadling)]
  23434. * Fix Bug #17220: command `pear help` outputs to stderr instead of stdout [dufuz]
  23435. * Fix Bug #17234: channel-discover adds port to HTTP Host header [dufuz]
  23436. * Fix Bug #17292: Code Coverage in PEAR_RunTest does not work with namespaces [sebastian]
  23437. * Fix Bug #17359: loadExtension() fails over missing dl() when used in multithread env [dufuz]
  23438. * Fix Bug #17378: pear info $package fails if directory with that name exists [dufuz]
  23439. </notes>
  23440. </release>
  23441. <release>
  23442. <date>2011-02-28</date>
  23443. <time>18:30:00</time>
  23444. <version>
  23445. <release>1.9.2</release>
  23446. <api>1.9.2</api>
  23447. </version>
  23448. <stability>
  23449. <release>stable</release>
  23450. <api>stable</api>
  23451. </stability>
  23452. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23453. <notes>
  23454. Important! This is a security fix release. The advisory can be found at
  23455. http://pear.php.net/advisory-20110228.txt
  23456. Bugs:
  23457. * Fixed Bug #17463: Regression: On Windows, svntag [patch by doconnor]
  23458. * Fixed Bug #17641: pecl-list doesn&apos;t sort packages by name [dufuz]
  23459. * Fixed Bug #17781: invalid argument warning on foreach due to an empty optional dependencie [dufuz]
  23460. * Fixed Bug #17801: PEAR run-tests wrongly detects php-cgi [patch by David Jean Louis (izi)]
  23461. * Fixed Bug #17839: pear svntag does not tag package.xml file [dufuz]
  23462. * Fixed Bug #17986: PEAR Installer cannot handle files moved between packages [dufuz]
  23463. * Fixed Bug #17997: Strange output if directories are not writeable [dufuz]
  23464. * Fixed Bug #18001: PEAR/RunTest coverage fails [dufuz]
  23465. * Fixed Bug #18056 [SECURITY]: Symlink attack in PEAR install [dufuz]
  23466. * Fixed Bug #18218: &quot;pear package&quot; does not allow the use of late static binding [dufuz and Christer Edvartsen]
  23467. * Fixed Bug #18238: Wrong return code from &quot;pear help&quot; [till]
  23468. * Fixed Bug #18308: Broken error message about missing channel validator [yunosh]
  23469. This feature is implemented as a result of #18056
  23470. * Implemented Request #16648: Use TMPDIR for builds instead of /var/tmp [dufuz]
  23471. </notes>
  23472. </release>
  23473. <release>
  23474. <date>2011-06-04</date>
  23475. <time>15:30:00</time>
  23476. <version>
  23477. <release>1.9.3</release>
  23478. <api>1.9.2</api>
  23479. </version>
  23480. <stability>
  23481. <release>stable</release>
  23482. <api>stable</api>
  23483. </stability>
  23484. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23485. <notes>
  23486. * Fixed Bug #17744: Empty changelog causes fatal error in setChangelogentry [dufuz]
  23487. * Fixed Bug #18340: raiseErro typo [doconnor]
  23488. * Fixed Bug #18349: package.xml version not recognized when single quoted [dufuz]
  23489. * Fixed Bug #18364: date.timezone errors for sh/bat files when TZ is not set in php.ini [dufuz]
  23490. * Fixed Bug #18388: Parentheses error in REST.php line 232 [dufuz]
  23491. * Fixed Bug #18428: invalid preg_match patterns [glen]
  23492. * Fixed Bug #18486: REST/10.php does not check error condition [dufuz]
  23493. * Fixed a problem in RunTest and code coverage. Correctly register the
  23494. code coverage shutdown function in case we are inside a namespace. [sebastian]
  23495. * Fixed a bug with extensions not providing their config.m4 and co in the root directory of
  23496. their pecl package but rather in a sub directory, such as xhprof. [dufuz]
  23497. </notes>
  23498. </release>
  23499. <release>
  23500. <date>2011-07-06</date>
  23501. <time>15:30:00</time>
  23502. <version>
  23503. <release>1.9.4</release>
  23504. <api>1.9.4</api>
  23505. </version>
  23506. <stability>
  23507. <release>stable</release>
  23508. <api>stable</api>
  23509. </stability>
  23510. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23511. <notes>
  23512. Bug Fixes:
  23513. * Bug #17350: &quot;pear install --force&quot; doesn&apos;t uninstall files from previous pkg versions [dufuz]
  23514. * Bug #18362: A whitespace TEMP_DIR path breaks install/upgrade functionality [dufuz]
  23515. * Bug #18440: bad tmp folder path on install : Unable to create path for C:/Program/tmp [dufuz]
  23516. * Bug #18581: &quot;config-get -c&quot; not returning channel&apos;s configuration when using alias [dufuz]
  23517. * Bug #18639: regression: installing xdebug fails most likely due to another fix [dufuz]
  23518. Features
  23519. * All System (the class) functions can now take in spaced paths as long as they are surrounded in quotes.
  23520. Prior to this it was possible to do that by passing all values in as an array (by product of #18362, #18440) [dufuz]
  23521. </notes>
  23522. </release>
  23523. <release>
  23524. <date>2014-06-27</date>
  23525. <time>18:17:00</time>
  23526. <version>
  23527. <release>1.9.5dev1</release>
  23528. <api>1.9.5</api>
  23529. </version>
  23530. <stability>
  23531. <release>devel</release>
  23532. <api>devel</api>
  23533. </stability>
  23534. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23535. <notes>
  23536. Bug fixes:
  23537. * Fix bug #18343: Entities in file names decoded during packaging [cweiske]
  23538. * Fix bug #18665: pecl extensions not enabled in empty php.ini files [Louis Opter]
  23539. * Fix bug #18834: Do not truncate cache file if it is a symlink [avb]
  23540. * Fix bug #18892: Parse error in Installer.php [ashnazg]
  23541. * Fix bug #19482: fix pearcmd for include paths with trailing backslash [cweiske]
  23542. * Fix bug #19793: PHP Notice about ob_end_clean() [cweiske]
  23543. * Fix bug #20086: Invalid regexp in PEAR_Builder::build() [avb]
  23544. * Fix bug #20203: split content-type and get real mime type [Samu Voutilainen]
  23545. * Fix bug #20283: use full path for &quot;zend_extension=...&quot; [cweiske]
  23546. * Fix bug #20284: Reset interpreter before running --CLEAN-- section php-cgi run [Mats Lindh]
  23547. * Fix bug #20285: fix spelling mistakes [Veres Lajos]
  23548. * Fix bug #20286: Support access of static variables on objects in validator [cweiske]
  23549. * Fix bug #20321: Correctly detect name of current user during installation [cweiske]
  23550. * Fix bug: let pear run-tests fail when there are failed tests [cweiske]
  23551. * Prepare a test for bug #18056 / bug #18834 [avb]
  23552. </notes>
  23553. </release>
  23554. <release>
  23555. <date>2014-07-12</date>
  23556. <time>14:22:23</time>
  23557. <version>
  23558. <release>1.9.5</release>
  23559. <api>1.9.5</api>
  23560. </version>
  23561. <stability>
  23562. <release>stable</release>
  23563. <api>stable</api>
  23564. </stability>
  23565. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23566. <notes>
  23567. No changes since 1.9.5.dev1.
  23568. Bug fixes in 1.9.5.dev1:
  23569. * Fix bug #18343: Entities in file names decoded during packaging [cweiske]
  23570. * Fix bug #18665: pecl extensions not enabled in empty php.ini files [Louis Opter]
  23571. * Fix bug #18834: Do not truncate cache file if it is a symlink [avb]
  23572. * Fix bug #18892: Parse error in Installer.php [ashnazg]
  23573. * Fix bug #19482: fix pearcmd for include paths with trailing backslash [cweiske]
  23574. * Fix bug #19793: PHP Notice about ob_end_clean() [cweiske]
  23575. * Fix bug #20086: Invalid regexp in PEAR_Builder::build() [avb]
  23576. * Fix bug #20203: split content-type and get real mime type [Samu Voutilainen]
  23577. * Fix bug #20283: use full path for &quot;zend_extension=...&quot; [cweiske]
  23578. * Fix bug #20284: Reset interpreter before running --CLEAN-- section php-cgi run [Mats Lindh]
  23579. * Fix bug #20285: fix spelling mistakes [Veres Lajos]
  23580. * Fix bug #20286: Support access of static variables on objects in validator [cweiske]
  23581. * Fix bug #20321: Correctly detect name of current user during installation [cweiske]
  23582. * Fix bug: let pear run-tests fail when there are failed tests [cweiske]
  23583. * Prepare a test for bug #18056 / bug #18834 [avb]
  23584. </notes>
  23585. </release>
  23586. <release>
  23587. <date>2015-07-25</date>
  23588. <time>13:42:42</time>
  23589. <version>
  23590. <release>1.10.0dev1</release>
  23591. <api>1.10.0</api>
  23592. </version>
  23593. <stability>
  23594. <release>devel</release>
  23595. <api>devel</api>
  23596. </stability>
  23597. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23598. <notes>
  23599. * Implement #20488: Add support for PHP 7 [cweiske]
  23600. * Drop support for PHP 4 and 5.0 - 5.3 [cweiske]
  23601. * Remove deprecated methods [cweiske]
  23602. * Fix static warnings [cweiske]
  23603. * Fix #17045: avoid overwriting include path [glen]
  23604. * Fix #17399: &quot;pear help&quot; doesn&apos;t mention the &quot;version&quot; command [kguest]
  23605. * Add --showdiff to &quot;pear run-tests&quot; to print diff for failed tests [tyrael]
  23606. * Fix channel.xml downloading from https if it did not change [cweiske]
  23607. </notes>
  23608. </release>
  23609. <release>
  23610. <date>2015-07-31</date>
  23611. <time>09:42:42</time>
  23612. <version>
  23613. <release>1.10.0dev2</release>
  23614. <api>1.10.0</api>
  23615. </version>
  23616. <stability>
  23617. <release>devel</release>
  23618. <api>devel</api>
  23619. </stability>
  23620. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23621. <notes>
  23622. * Fix #18638 and #18405: Make PEAR::loadExtension static [cweiske]
  23623. * Fix #20319: allow pear to work when cache_dir is not writable [remicollet]
  23624. * Implement #20333: New role=man for man pages [bjori]
  23625. * Implement #20334: add &quot;metadata_dir&quot; configuration option [remicollet]
  23626. * Add long option names to install-pear.php [remicollet]
  23627. </notes>
  23628. </release>
  23629. <release>
  23630. <date>2015-09-28</date>
  23631. <time>09:42:42</time>
  23632. <version>
  23633. <release>1.10.0dev3</release>
  23634. <api>1.10.0</api>
  23635. </version>
  23636. <stability>
  23637. <release>devel</release>
  23638. <api>devel</api>
  23639. </stability>
  23640. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23641. <notes>
  23642. * Fix #20507: pear list-upgrades does not take PHP version into account [cweiske]
  23643. * Fix #20927: Use correct php-config [cweiske]
  23644. * Fix #20946: PEAR_Builder::log() declaration [remicollet]
  23645. * Remove PEAR/ErrorStack5.php [cweiske]
  23646. </notes>
  23647. </release>
  23648. <release>
  23649. <date>2015-10-07</date>
  23650. <time>11:22:42</time>
  23651. <version>
  23652. <release>1.10.0</release>
  23653. <api>1.10.0</api>
  23654. </version>
  23655. <stability>
  23656. <release>stable</release>
  23657. <api>stable</api>
  23658. </stability>
  23659. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23660. <notes>
  23661. No changes since version 1.10.0dev3.
  23662. Changes since version 1.9.5:
  23663. * Implement #20488: Add support for PHP 7 [cweiske]
  23664. * Drop support for PHP 4 and 5.0 - 5.3 [cweiske]
  23665. * Remove deprecated methods [cweiske]
  23666. * Add --showdiff to &quot;pear run-tests&quot; to print diff for failed tests [tyrael]
  23667. * Implement #20333: New role=man for man pages [bjori]
  23668. * Implement #20334: add &quot;metadata_dir&quot; configuration option [remicollet]
  23669. * Add long option names to install-pear.php [remicollet]
  23670. * Remove PEAR/ErrorStack5.php [cweiske]
  23671. * Fix #17045: avoid overwriting include path [glen]
  23672. * Fix #17399: &quot;pear help&quot; doesn&apos;t mention the &quot;version&quot; command [kguest]
  23673. * Fix #18638 and #18405: Make PEAR::loadExtension static [cweiske]
  23674. * Fix #20319: allow pear to work when cache_dir is not writable [remicollet]
  23675. * Fix #20507: pear list-upgrades does not take PHP version into account [cweiske]
  23676. * Fix #20927: Use correct php-config [cweiske]
  23677. * Fix #20946: PEAR_Builder::log() declaration [remicollet]
  23678. * Fix channel.xml downloading from https if it did not change [cweiske]
  23679. * Fix static warnings [cweiske]
  23680. </notes>
  23681. </release>
  23682. <release>
  23683. <date>2015-10-17</date>
  23684. <time>13:22:42</time>
  23685. <version>
  23686. <release>1.10.1</release>
  23687. <api>1.10.1</api>
  23688. </version>
  23689. <stability>
  23690. <release>stable</release>
  23691. <api>stable</api>
  23692. </stability>
  23693. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23694. <notes>
  23695. * Fix bug #20959: Crash on channel discovery with channel.xml redirect [cweiske]
  23696. * Fix bug #20968: Incorrect call to __construct() from PEAR() [edlman]
  23697. * Add legacy constructor for PEAR_Error for backwards compatibility [cweiske]
  23698. </notes>
  23699. </release>
  23700. <release>
  23701. <date>2017-02-28</date>
  23702. <time>07:40:00</time>
  23703. <version>
  23704. <release>1.10.2</release>
  23705. <api>1.10.1</api>
  23706. </version>
  23707. <stability>
  23708. <release>stable</release>
  23709. <api>stable</api>
  23710. </stability>
  23711. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23712. <notes>
  23713. * Fix Bug #4426: PEAR_Autoloader __call() must take only 2 arguments [kna]
  23714. * Fix Bug #20989: fatal error/bug in the postinstallscript task [kguest]
  23715. * Fix Bug #20991: Strict Standards: startSession and run methods in PEAR_Task_Postinstallscript [kguest]
  23716. * Fix Bug #21001: PEAR_ERROR_DIE exit code is 0 [danielc]
  23717. * Pull Request #52: Channel&apos;s _lastmodified is an int and not a string [sathieu]
  23718. * Pull Request #53: Add proper HTTPS proxy support through the CONNECT verb [youknow0]
  23719. * Pull Request #58: Make method signatures compatible. [yunosh]
  23720. </notes>
  23721. </release>
  23722. <release>
  23723. <date>2017-02-28</date>
  23724. <time>10:15:00</time>
  23725. <version>
  23726. <release>1.10.3</release>
  23727. <api>1.10.1</api>
  23728. </version>
  23729. <stability>
  23730. <release>stable</release>
  23731. <api>stable</api>
  23732. </stability>
  23733. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23734. <notes>
  23735. * Bug #21188: Class &apos;PEAR_Proxy&apos; not found
  23736. </notes>
  23737. </release>
  23738. <release>
  23739. <date>2017-04-25</date>
  23740. <version>
  23741. <release>1.10.4</release>
  23742. <api>1.10.1</api>
  23743. </version>
  23744. <stability>
  23745. <release>stable</release>
  23746. <api>stable</api>
  23747. </stability>
  23748. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23749. <notes>
  23750. * Bug #18102: pear install does not fail on error
  23751. </notes>
  23752. </release>
  23753. <release>
  23754. <date>2017-06-27</date>
  23755. <version>
  23756. <release>1.10.5</release>
  23757. <api>1.10.1</api>
  23758. </version>
  23759. <stability>
  23760. <release>stable</release>
  23761. <api>stable</api>
  23762. </stability>
  23763. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23764. <notes>
  23765. * Bug #21222: PHP 7.2 compatibility: Upgrade to Archive_Tar 1.4.3 needed
  23766. </notes>
  23767. </release>
  23768. <release>
  23769. <date>2018-08-22</date>
  23770. <version>
  23771. <release>1.10.6</release>
  23772. <api>1.10.1</api>
  23773. </version>
  23774. <stability>
  23775. <release>stable</release>
  23776. <api>stable</api>
  23777. </stability>
  23778. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23779. <notes>
  23780. * PR #70: Fix notice undefined variable metadata_dir
  23781. * PR #71: fix Warning: count(): Parameter must be an array or an object
  23782. * PR #74: Bug #23744 Remove is_executable check
  23783. * Bug #23744: The is_executable check in the Which method when run on Windows is unnecessary
  23784. * PR #75: Migrate old while(list() = each()) constructs to foreach
  23785. * PR #76: Fix PHP Warning: &quot;continue&quot; targeting switch is equivalent to &quot;break&quot;
  23786. * PR #77: proxy server auth
  23787. * PR #72: Correctly authenticate at proxy server
  23788. * PR #78: array or Countable error in 7.2
  23789. </notes>
  23790. </release>
  23791. <release>
  23792. <date>2018-12-05</date>
  23793. <version>
  23794. <release>1.10.7</release>
  23795. <api>1.10.1</api>
  23796. </version>
  23797. <stability>
  23798. <release>stable</release>
  23799. <api>stable</api>
  23800. </stability>
  23801. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23802. <notes>
  23803. * PR #79: Prevent Unable to find the wrapper &quot;channel&quot; Warning
  23804. * PR #80: fix Warning: &quot;continue&quot; targeting switch is equivalent to &quot;break&quot;. Did you mean to use &quot;continue 2&quot;
  23805. * PR #81: Add flags to PECL shell script for shared extensions
  23806. </notes>
  23807. </release>
  23808. <release>
  23809. <date>2019-02-07</date>
  23810. <version>
  23811. <release>1.10.8</release>
  23812. <api>1.10.1</api>
  23813. </version>
  23814. <stability>
  23815. <release>stable</release>
  23816. <api>stable</api>
  23817. </stability>
  23818. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23819. <notes>
  23820. * PR #83: Drop track_errors from options
  23821. * PR #84: Fix PHP 8 compatibility issues
  23822. </notes>
  23823. </release>
  23824. <release>
  23825. <date>2019-03-13</date>
  23826. <version>
  23827. <release>1.10.9</release>
  23828. <api>1.10.1</api>
  23829. </version>
  23830. <stability>
  23831. <release>stable</release>
  23832. <api>stable</api>
  23833. </stability>
  23834. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23835. <notes>
  23836. * PR #85: Fixes static calls for PHP 8
  23837. * PR #86: Adjust silencing check for PHP 8
  23838. * PR #87: Comparison fixes
  23839. * PR #88: Only add bin_dir to PATH if not already there (fixes PHP Bug #75852)
  23840. </notes>
  23841. </release>
  23842. <release>
  23843. <date>2019-11-19</date>
  23844. <version>
  23845. <release>1.10.10</release>
  23846. <api>1.10.1</api>
  23847. </version>
  23848. <stability>
  23849. <release>stable</release>
  23850. <api>stable</api>
  23851. </stability>
  23852. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23853. <notes>
  23854. * PR #89: Fix scripts/* include paths
  23855. * PR #90: Non-interactive configureoption answers
  23856. * PR #91: Added missing preg quote
  23857. * PR #92: handle &quot;lib64&quot; case for glibc detection
  23858. * PR #93: Fix PHP Notice: Trying to access array offset on value of type bool with 7.4
  23859. * PR #94: Updated logic in useLocalCache to reuse getCacheId
  23860. * PR #95: Fix manpage warning
  23861. * PR #96: Implement the SOURCE_DATE_EPOCH specification
  23862. * PR #97: Fix PHP 7.4 deprecation: array/string curly braces access
  23863. * PR #98: Fix use of null/false as array
  23864. * PR #99: Fix Travis builds on PHP 5.4 and 5.5
  23865. * PR #100: Honor PHP temp directory config
  23866. * PR #101: Fix documentation: the `--force` is required
  23867. </notes>
  23868. </release>
  23869. <release>
  23870. <date>2020-04-10</date>
  23871. <version>
  23872. <release>1.10.11</release>
  23873. <api>1.10.1</api>
  23874. </version>
  23875. <stability>
  23876. <release>stable</release>
  23877. <api>stable</api>
  23878. </stability>
  23879. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23880. <notes>
  23881. * PR #102: Fix logging error for urls not in cache
  23882. * PR #103: Fix undefined constant name
  23883. * PR #105: Sort list of packages
  23884. * PR #106: Update REST.php
  23885. * PR #107: Update .travis.yml to include PHP 7.4
  23886. * PR #108: Remove unneeded code
  23887. </notes>
  23888. </release>
  23889. <release>
  23890. <date>2020-04-19</date>
  23891. <version>
  23892. <release>1.10.12</release>
  23893. <api>1.10.1</api>
  23894. </version>
  23895. <stability>
  23896. <release>stable</release>
  23897. <api>stable</api>
  23898. </stability>
  23899. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23900. <notes>
  23901. * adjust dependencies based on new releases
  23902. </notes>
  23903. </release>
  23904. <release>
  23905. <date>2021-08-10</date>
  23906. <version>
  23907. <release>1.10.13</release>
  23908. <api>1.10.1</api>
  23909. </version>
  23910. <stability>
  23911. <release>stable</release>
  23912. <api>stable</api>
  23913. </stability>
  23914. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23915. <notes>
  23916. * PR #114: unsupported protocol - use --force to continue
  23917. * PR #117: Add $this operator to _determineIfPowerpc calls
  23918. </notes>
  23919. </release>
  23920. <release>
  23921. <date>2023-11-26</date>
  23922. <version>
  23923. <release>1.10.14</release>
  23924. <api>1.10.1</api>
  23925. </version>
  23926. <stability>
  23927. <release>stable</release>
  23928. <api>stable</api>
  23929. </stability>
  23930. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23931. <notes>
  23932. * PR #112: Put glue and pieces parameters to implode in correct order for PHP 7.4+
  23933. * PR #121: Fix PHP bug 81653: Typo in install-pear-nozlib.phar
  23934. * PR #122: add %S EXPECTF capability
  23935. * PR #124: Fix: Creation of dynamic property PEAR_Error::$callback is deprecated
  23936. * PR #125: Fixed extension loaded check for pecl binaries
  23937. * PR #126: Remove -n option from pecl.bat for shared extensions
  23938. * PR #127: fix Using ${var} in strings is deprecated
  23939. * PR #128: fix lingering license references to PHP license
  23940. * PR #129: Exclude tests from composer classmap
  23941. * PR #131: fix private lastError name
  23942. </notes>
  23943. </release>
  23944. <release>
  23945. <date>2024-03-09</date>
  23946. <version>
  23947. <release>1.10.15</release>
  23948. <api>1.10.1</api>
  23949. </version>
  23950. <stability>
  23951. <release>stable</release>
  23952. <api>stable</api>
  23953. </stability>
  23954. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23955. <notes>
  23956. * PR #132: cleanup uneeded test
  23957. * PR #135: Fix PHP Deprecated: Calling get_class() without arguments
  23958. </notes>
  23959. </release>
  23960. <release>
  23961. <date>2024-11-24</date>
  23962. <version>
  23963. <release>1.10.16</release>
  23964. <api>1.10.1</api>
  23965. </version>
  23966. <stability>
  23967. <release>stable</release>
  23968. <api>stable</api>
  23969. </stability>
  23970. <license uri="http://opensource.org/licenses/bsd-license.php">New BSD License</license>
  23971. <notes>
  23972. * PR #141: Fix bug #27796: &quot;Array to string&quot; conversion warnings on installs/other actions
  23973. * PR #145: Never reference E_STRICT on PHP 8.4+
  23974. * PR #147: Fix tests 8.1+
  23975. </notes>
  23976. </release>
  23977. </changelog>
  23978. </package>
  23979. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/OS/Guess.php���������������������������������������������������������������������������0000664�0001750�0001750�00000026376�14720722517�014426� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  23980. /**
  23981. * The OS_Guess class
  23982. *
  23983. * PHP versions 4 and 5
  23984. *
  23985. * @category pear
  23986. * @package PEAR
  23987. * @author Stig Bakken <ssb@php.net>
  23988. * @author Gregory Beaver <cellog@php.net>
  23989. * @copyright 1997-2009 The Authors
  23990. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  23991. * @link http://pear.php.net/package/PEAR
  23992. * @since File available since PEAR 0.1
  23993. */
  23994. // {{{ uname examples
  23995. // php_uname() without args returns the same as 'uname -a', or a PHP-custom
  23996. // string for Windows.
  23997. // PHP versions prior to 4.3 return the uname of the host where PHP was built,
  23998. // as of 4.3 it returns the uname of the host running the PHP code.
  23999. //
  24000. // PC RedHat Linux 7.1:
  24001. // Linux host.example.com 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686 unknown
  24002. //
  24003. // PC Debian Potato:
  24004. // Linux host 2.4.17 #2 SMP Tue Feb 12 15:10:04 CET 2002 i686 unknown
  24005. //
  24006. // PC FreeBSD 3.3:
  24007. // FreeBSD host.example.com 3.3-STABLE FreeBSD 3.3-STABLE #0: Mon Feb 21 00:42:31 CET 2000 root@example.com:/usr/src/sys/compile/CONFIG i386
  24008. //
  24009. // PC FreeBSD 4.3:
  24010. // FreeBSD host.example.com 4.3-RELEASE FreeBSD 4.3-RELEASE #1: Mon Jun 25 11:19:43 EDT 2001 root@example.com:/usr/src/sys/compile/CONFIG i386
  24011. //
  24012. // PC FreeBSD 4.5:
  24013. // FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb 6 23:59:23 CET 2002 root@example.com:/usr/src/sys/compile/CONFIG i386
  24014. //
  24015. // PC FreeBSD 4.5 w/uname from GNU shellutils:
  24016. // FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb i386 unknown
  24017. //
  24018. // HP 9000/712 HP-UX 10:
  24019. // HP-UX iq B.10.10 A 9000/712 2008429113 two-user license
  24020. //
  24021. // HP 9000/712 HP-UX 10 w/uname from GNU shellutils:
  24022. // HP-UX host B.10.10 A 9000/712 unknown
  24023. //
  24024. // IBM RS6000/550 AIX 4.3:
  24025. // AIX host 3 4 000003531C00
  24026. //
  24027. // AIX 4.3 w/uname from GNU shellutils:
  24028. // AIX host 3 4 000003531C00 unknown
  24029. //
  24030. // SGI Onyx IRIX 6.5 w/uname from GNU shellutils:
  24031. // IRIX64 host 6.5 01091820 IP19 mips
  24032. //
  24033. // SGI Onyx IRIX 6.5:
  24034. // IRIX64 host 6.5 01091820 IP19
  24035. //
  24036. // SparcStation 20 Solaris 8 w/uname from GNU shellutils:
  24037. // SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc
  24038. //
  24039. // SparcStation 20 Solaris 8:
  24040. // SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc SUNW,SPARCstation-20
  24041. //
  24042. // Mac OS X (Darwin)
  24043. // Darwin home-eden.local 7.5.0 Darwin Kernel Version 7.5.0: Thu Aug 5 19:26:16 PDT 2004; root:xnu/xnu-517.7.21.obj~3/RELEASE_PPC Power Macintosh
  24044. //
  24045. // Mac OS X early versions
  24046. //
  24047. // }}}
  24048. /* TODO:
  24049. * - define endianness, to allow matchSignature("bigend") etc.
  24050. */
  24051. /**
  24052. * Retrieves information about the current operating system
  24053. *
  24054. * This class uses php_uname() to grok information about the current OS
  24055. *
  24056. * @category pear
  24057. * @package PEAR
  24058. * @author Stig Bakken <ssb@php.net>
  24059. * @author Gregory Beaver <cellog@php.net>
  24060. * @copyright 1997-2020 The Authors
  24061. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  24062. * @version Release: 1.10.16
  24063. * @link http://pear.php.net/package/PEAR
  24064. * @since Class available since Release 0.1
  24065. */
  24066. class OS_Guess
  24067. {
  24068. var $sysname;
  24069. var $nodename;
  24070. var $cpu;
  24071. var $release;
  24072. var $extra;
  24073. function __construct($uname = null)
  24074. {
  24075. list($this->sysname,
  24076. $this->release,
  24077. $this->cpu,
  24078. $this->extra,
  24079. $this->nodename) = $this->parseSignature($uname);
  24080. }
  24081. function parseSignature($uname = null)
  24082. {
  24083. static $sysmap = array(
  24084. 'HP-UX' => 'hpux',
  24085. 'IRIX64' => 'irix',
  24086. );
  24087. static $cpumap = array(
  24088. 'i586' => 'i386',
  24089. 'i686' => 'i386',
  24090. 'ppc' => 'powerpc',
  24091. );
  24092. if ($uname === null) {
  24093. $uname = php_uname();
  24094. }
  24095. $parts = preg_split('/\s+/', trim($uname));
  24096. $n = count($parts);
  24097. $release = $machine = $cpu = '';
  24098. $sysname = $parts[0];
  24099. $nodename = $parts[1];
  24100. $cpu = $parts[$n-1];
  24101. $extra = '';
  24102. if ($cpu == 'unknown') {
  24103. $cpu = $parts[$n - 2];
  24104. }
  24105. switch ($sysname) {
  24106. case 'AIX' :
  24107. $release = "$parts[3].$parts[2]";
  24108. break;
  24109. case 'Windows' :
  24110. $release = $parts[1];
  24111. if ($release == '95/98') {
  24112. $release = '9x';
  24113. }
  24114. $cpu = 'i386';
  24115. break;
  24116. case 'Linux' :
  24117. $extra = $this->_detectGlibcVersion();
  24118. // use only the first two digits from the kernel version
  24119. $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]);
  24120. break;
  24121. case 'Mac' :
  24122. $sysname = 'darwin';
  24123. $nodename = $parts[2];
  24124. $release = $parts[3];
  24125. $cpu = $this->_determineIfPowerpc($cpu, $parts);
  24126. break;
  24127. case 'Darwin' :
  24128. $cpu = $this->_determineIfPowerpc($cpu, $parts);
  24129. $release = preg_replace('/^([0-9]+\.[0-9]+).*/', '\1', $parts[2]);
  24130. break;
  24131. default:
  24132. $release = preg_replace('/-.*/', '', $parts[2]);
  24133. break;
  24134. }
  24135. if (isset($sysmap[$sysname])) {
  24136. $sysname = $sysmap[$sysname];
  24137. } else {
  24138. $sysname = strtolower($sysname);
  24139. }
  24140. if (isset($cpumap[$cpu])) {
  24141. $cpu = $cpumap[$cpu];
  24142. }
  24143. return array($sysname, $release, $cpu, $extra, $nodename);
  24144. }
  24145. function _determineIfPowerpc($cpu, $parts)
  24146. {
  24147. $n = count($parts);
  24148. if ($cpu == 'Macintosh' && $parts[$n - 2] == 'Power') {
  24149. $cpu = 'powerpc';
  24150. }
  24151. return $cpu;
  24152. }
  24153. function _detectGlibcVersion()
  24154. {
  24155. static $glibc = false;
  24156. if ($glibc !== false) {
  24157. return $glibc; // no need to run this multiple times
  24158. }
  24159. $major = $minor = 0;
  24160. include_once "System.php";
  24161. // Let's try reading possible libc.so.6 symlinks
  24162. $libcs = array(
  24163. '/lib64/libc.so.6',
  24164. '/lib/libc.so.6',
  24165. '/lib/i386-linux-gnu/libc.so.6'
  24166. );
  24167. $versions = array();
  24168. foreach ($libcs as $file) {
  24169. $versions = $this->_readGlibCVersionFromSymlink($file);
  24170. if ($versions != []) {
  24171. list($major, $minor) = $versions;
  24172. break;
  24173. }
  24174. }
  24175. // Use glibc's <features.h> header file to
  24176. // get major and minor version number:
  24177. if (!($major && $minor)) {
  24178. $versions = $this->_readGlibCVersionFromFeaturesHeaderFile();
  24179. }
  24180. if (is_array($versions) && $versions != []) {
  24181. list($major, $minor) = $versions;
  24182. }
  24183. if (!($major && $minor)) {
  24184. return $glibc = '';
  24185. }
  24186. return $glibc = "glibc{$major}.{$minor}";
  24187. }
  24188. function _readGlibCVersionFromSymlink($file)
  24189. {
  24190. $versions = array();
  24191. if (@is_link($file)
  24192. && (preg_match('/^libc-(.*)\.so$/', basename(readlink($file)), $matches))
  24193. ) {
  24194. $versions = explode('.', $matches[1]);
  24195. }
  24196. return $versions;
  24197. }
  24198. function _readGlibCVersionFromFeaturesHeaderFile()
  24199. {
  24200. $features_header_file = '/usr/include/features.h';
  24201. if (!(@file_exists($features_header_file)
  24202. && @is_readable($features_header_file))
  24203. ) {
  24204. return array();
  24205. }
  24206. if (!@file_exists('/usr/bin/cpp') || !@is_executable('/usr/bin/cpp')) {
  24207. return $this->_parseFeaturesHeaderFile($features_header_file);
  24208. } // no cpp
  24209. return $this->_fromGlibCTest();
  24210. }
  24211. function _parseFeaturesHeaderFile($features_header_file)
  24212. {
  24213. $features_file = fopen($features_header_file, 'rb');
  24214. while (!feof($features_file)) {
  24215. $line = fgets($features_file, 8192);
  24216. if (!$this->_IsADefinition($line)) {
  24217. continue;
  24218. }
  24219. if (strpos($line, '__GLIBC__')) {
  24220. // major version number #define __GLIBC__ version
  24221. $line = preg_split('/\s+/', $line);
  24222. $glibc_major = trim($line[2]);
  24223. if (isset($glibc_minor)) {
  24224. break;
  24225. }
  24226. continue;
  24227. }
  24228. if (strpos($line, '__GLIBC_MINOR__')) {
  24229. // got the minor version number
  24230. // #define __GLIBC_MINOR__ version
  24231. $line = preg_split('/\s+/', $line);
  24232. $glibc_minor = trim($line[2]);
  24233. if (isset($glibc_major)) {
  24234. break;
  24235. }
  24236. }
  24237. }
  24238. fclose($features_file);
  24239. if (!isset($glibc_major) || !isset($glibc_minor)) {
  24240. return array();
  24241. }
  24242. return array(trim($glibc_major), trim($glibc_minor));
  24243. }
  24244. function _IsADefinition($line)
  24245. {
  24246. if ($line === false) {
  24247. return false;
  24248. }
  24249. return strpos(trim($line), '#define') !== false;
  24250. }
  24251. function _fromGlibCTest()
  24252. {
  24253. $major = null;
  24254. $minor = null;
  24255. $tmpfile = System::mktemp("glibctest");
  24256. $fp = fopen($tmpfile, "w");
  24257. fwrite($fp, "#include <features.h>\n__GLIBC__ __GLIBC_MINOR__\n");
  24258. fclose($fp);
  24259. $cpp = popen("/usr/bin/cpp $tmpfile", "r");
  24260. while ($line = fgets($cpp, 1024)) {
  24261. if ($line[0] == '#' || trim($line) == '') {
  24262. continue;
  24263. }
  24264. if (list($major, $minor) = explode(' ', trim($line))) {
  24265. break;
  24266. }
  24267. }
  24268. pclose($cpp);
  24269. unlink($tmpfile);
  24270. if ($major !== null && $minor !== null) {
  24271. return [$major, $minor];
  24272. }
  24273. }
  24274. function getSignature()
  24275. {
  24276. if (empty($this->extra)) {
  24277. return "{$this->sysname}-{$this->release}-{$this->cpu}";
  24278. }
  24279. return "{$this->sysname}-{$this->release}-{$this->cpu}-{$this->extra}";
  24280. }
  24281. function getSysname()
  24282. {
  24283. return $this->sysname;
  24284. }
  24285. function getNodename()
  24286. {
  24287. return $this->nodename;
  24288. }
  24289. function getCpu()
  24290. {
  24291. return $this->cpu;
  24292. }
  24293. function getRelease()
  24294. {
  24295. return $this->release;
  24296. }
  24297. function getExtra()
  24298. {
  24299. return $this->extra;
  24300. }
  24301. function matchSignature($match)
  24302. {
  24303. $fragments = is_array($match) ? $match : explode('-', $match);
  24304. $n = count($fragments);
  24305. $matches = 0;
  24306. if ($n > 0) {
  24307. $matches += $this->_matchFragment($fragments[0], $this->sysname);
  24308. }
  24309. if ($n > 1) {
  24310. $matches += $this->_matchFragment($fragments[1], $this->release);
  24311. }
  24312. if ($n > 2) {
  24313. $matches += $this->_matchFragment($fragments[2], $this->cpu);
  24314. }
  24315. if ($n > 3) {
  24316. $matches += $this->_matchFragment($fragments[3], $this->extra);
  24317. }
  24318. return ($matches == $n);
  24319. }
  24320. function _matchFragment($fragment, $value)
  24321. {
  24322. if (strcspn($fragment, '*?') < strlen($fragment)) {
  24323. $expression = str_replace(
  24324. array('*', '?', '/'),
  24325. array('.*', '.', '\\/'),
  24326. $fragment
  24327. );
  24328. $reg = '/^' . $expression . '\\z/';
  24329. return preg_match($reg, $value);
  24330. }
  24331. return ($fragment == '*' || !strcasecmp($fragment, $value));
  24332. }
  24333. }
  24334. /*
  24335. * Local Variables:
  24336. * indent-tabs-mode: nil
  24337. * c-basic-offset: 4
  24338. * End:
  24339. */
  24340. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/ChannelFile/Parser.php������������������������������������������������������������0000644�0001750�0001750�00000003254�14720722517�017136� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  24341. /**
  24342. * PEAR_ChannelFile_Parser for parsing channel.xml
  24343. *
  24344. * PHP versions 4 and 5
  24345. *
  24346. * @category pear
  24347. * @package PEAR
  24348. * @author Greg Beaver <cellog@php.net>
  24349. * @copyright 1997-2009 The Authors
  24350. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  24351. * @link http://pear.php.net/package/PEAR
  24352. * @since File available since Release 1.4.0a1
  24353. */
  24354. /**
  24355. * base xml parser class
  24356. */
  24357. require_once 'PEAR/XMLParser.php';
  24358. require_once 'PEAR/ChannelFile.php';
  24359. /**
  24360. * Parser for channel.xml
  24361. * @category pear
  24362. * @package PEAR
  24363. * @author Greg Beaver <cellog@php.net>
  24364. * @copyright 1997-2009 The Authors
  24365. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  24366. * @version Release: 1.10.16
  24367. * @link http://pear.php.net/package/PEAR
  24368. * @since Class available since Release 1.4.0a1
  24369. */
  24370. class PEAR_ChannelFile_Parser extends PEAR_XMLParser
  24371. {
  24372. var $_config;
  24373. var $_logger;
  24374. var $_registry;
  24375. function setConfig(&$c)
  24376. {
  24377. $this->_config = &$c;
  24378. $this->_registry = &$c->getRegistry();
  24379. }
  24380. function setLogger(&$l)
  24381. {
  24382. $this->_logger = &$l;
  24383. }
  24384. function parse($data, $file)
  24385. {
  24386. if (PEAR::isError($err = parent::parse($data, $file))) {
  24387. return $err;
  24388. }
  24389. $ret = new PEAR_ChannelFile;
  24390. $ret->setConfig($this->_config);
  24391. if (isset($this->_logger)) {
  24392. $ret->setLogger($this->_logger);
  24393. }
  24394. $ret->fromArray($this->_unserializedData);
  24395. // make sure the filelist is in the easy to read format needed
  24396. $ret->flattenFilelist();
  24397. $ret->setPackagefile($file, $archive);
  24398. return $ret;
  24399. }
  24400. }����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Auth.xml������������������������������������������������������������������0000644�0001750�0001750�00000002314�14720722517�016016� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<commands version="1.0">
  24401. <login>
  24402. <summary>Connects and authenticates to remote server [Deprecated in favor of channel-login]</summary>
  24403. <function>doLogin</function>
  24404. <shortcut>li</shortcut>
  24405. <options />
  24406. <doc>&lt;channel name&gt;
  24407. WARNING: This function is deprecated in favor of using channel-login
  24408. Log in to a remote channel server. If &lt;channel name&gt; is not supplied,
  24409. the default channel is used. To use remote functions in the installer
  24410. that require any kind of privileges, you need to log in first. The
  24411. username and password you enter here will be stored in your per-user
  24412. PEAR configuration (~/.pearrc on Unix-like systems). After logging
  24413. in, your username and password will be sent along in subsequent
  24414. operations on the remote server.</doc>
  24415. </login>
  24416. <logout>
  24417. <summary>Logs out from the remote server [Deprecated in favor of channel-logout]</summary>
  24418. <function>doLogout</function>
  24419. <shortcut>lo</shortcut>
  24420. <options />
  24421. <doc>
  24422. WARNING: This function is deprecated in favor of using channel-logout
  24423. Logs out from the remote server. This command does not actually
  24424. connect to the remote server, it only deletes the stored username and
  24425. password from your user configuration.</doc>
  24426. </logout>
  24427. </commands>��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Auth.php������������������������������������������������������������������0000644�0001750�0001750�00000005013�14720722517�016004� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  24428. /**
  24429. * PEAR_Command_Auth (login, logout commands)
  24430. *
  24431. * PHP versions 4 and 5
  24432. *
  24433. * @category pear
  24434. * @package PEAR
  24435. * @author Stig Bakken <ssb@php.net>
  24436. * @author Greg Beaver <cellog@php.net>
  24437. * @copyright 1997-2009 The Authors
  24438. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  24439. * @link http://pear.php.net/package/PEAR
  24440. * @since File available since Release 0.1
  24441. * @deprecated since 1.8.0alpha1
  24442. */
  24443. /**
  24444. * base class
  24445. */
  24446. require_once 'PEAR/Command/Channels.php';
  24447. /**
  24448. * PEAR commands for login/logout
  24449. *
  24450. * @category pear
  24451. * @package PEAR
  24452. * @author Stig Bakken <ssb@php.net>
  24453. * @author Greg Beaver <cellog@php.net>
  24454. * @copyright 1997-2009 The Authors
  24455. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  24456. * @version Release: 1.10.16
  24457. * @link http://pear.php.net/package/PEAR
  24458. * @since Class available since Release 0.1
  24459. * @deprecated since 1.8.0alpha1
  24460. */
  24461. class PEAR_Command_Auth extends PEAR_Command_Channels
  24462. {
  24463. var $commands = array(
  24464. 'login' => array(
  24465. 'summary' => 'Connects and authenticates to remote server [Deprecated in favor of channel-login]',
  24466. 'shortcut' => 'li',
  24467. 'function' => 'doLogin',
  24468. 'options' => array(),
  24469. 'doc' => '<channel name>
  24470. WARNING: This function is deprecated in favor of using channel-login
  24471. Log in to a remote channel server. If <channel name> is not supplied,
  24472. the default channel is used. To use remote functions in the installer
  24473. that require any kind of privileges, you need to log in first. The
  24474. username and password you enter here will be stored in your per-user
  24475. PEAR configuration (~/.pearrc on Unix-like systems). After logging
  24476. in, your username and password will be sent along in subsequent
  24477. operations on the remote server.',
  24478. ),
  24479. 'logout' => array(
  24480. 'summary' => 'Logs out from the remote server [Deprecated in favor of channel-logout]',
  24481. 'shortcut' => 'lo',
  24482. 'function' => 'doLogout',
  24483. 'options' => array(),
  24484. 'doc' => '
  24485. WARNING: This function is deprecated in favor of using channel-logout
  24486. Logs out from the remote server. This command does not actually
  24487. connect to the remote server, it only deletes the stored username and
  24488. password from your user configuration.',
  24489. )
  24490. );
  24491. /**
  24492. * PEAR_Command_Auth constructor.
  24493. *
  24494. * @access public
  24495. */
  24496. function __construct(&$ui, &$config)
  24497. {
  24498. parent::__construct($ui, $config);
  24499. }
  24500. }
  24501. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Build.xml�����������������������������������������������������������������0000664�0001750�0001750�00000000604�14720722517�016156� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<commands version="1.0">
  24502. <build>
  24503. <summary>Build an Extension From C Source</summary>
  24504. <function>doBuild</function>
  24505. <shortcut>b</shortcut>
  24506. <options>
  24507. <configureoptions>
  24508. <shortopt>D</shortopt>
  24509. <arg>OPTION1=VALUE[ OPTION2=VALUE]</arg>
  24510. </configureoptions>
  24511. </options>
  24512. <doc>[package.xml]
  24513. Builds one or more extensions contained in a package.</doc>
  24514. </build>
  24515. </commands>����������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Build.php�����������������������������������������������������������������0000664�0001750�0001750�00000005123�14720722517�016146� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  24516. /**
  24517. * PEAR_Command_Auth (build command)
  24518. *
  24519. * PHP versions 4 and 5
  24520. *
  24521. * @category pear
  24522. * @package PEAR
  24523. * @author Stig Bakken <ssb@php.net>
  24524. * @author Tomas V.V.Cox <cox@idecnet.com>
  24525. * @author Greg Beaver <cellog@php.net>
  24526. * @copyright 1997-2009 The Authors
  24527. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  24528. * @link http://pear.php.net/package/PEAR
  24529. * @since File available since Release 0.1
  24530. */
  24531. /**
  24532. * base class
  24533. */
  24534. require_once 'PEAR/Command/Common.php';
  24535. /**
  24536. * PEAR commands for building extensions.
  24537. *
  24538. * @category pear
  24539. * @package PEAR
  24540. * @author Stig Bakken <ssb@php.net>
  24541. * @author Tomas V.V.Cox <cox@idecnet.com>
  24542. * @author Greg Beaver <cellog@php.net>
  24543. * @copyright 1997-2009 The Authors
  24544. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  24545. * @version Release: 1.10.16
  24546. * @link http://pear.php.net/package/PEAR
  24547. * @since Class available since Release 0.1
  24548. */
  24549. class PEAR_Command_Build extends PEAR_Command_Common
  24550. {
  24551. var $commands = array(
  24552. 'build' => array(
  24553. 'summary' => 'Build an Extension From C Source',
  24554. 'function' => 'doBuild',
  24555. 'shortcut' => 'b',
  24556. 'options' => array(
  24557. 'configureoptions' => array(
  24558. 'shortopt' => 'D',
  24559. 'arg' => 'OPTION1=VALUE[ OPTION2=VALUE]',
  24560. 'doc' => 'space-delimited list of configure options',
  24561. ),
  24562. ),
  24563. 'doc' => '[package.xml]
  24564. Builds one or more extensions contained in a package.'
  24565. ),
  24566. );
  24567. /**
  24568. * PEAR_Command_Build constructor.
  24569. *
  24570. * @access public
  24571. */
  24572. function __construct(&$ui, &$config)
  24573. {
  24574. parent::__construct($ui, $config);
  24575. }
  24576. function doBuild($command, $options, $params)
  24577. {
  24578. require_once 'PEAR/Builder.php';
  24579. if (sizeof($params) < 1) {
  24580. $params[0] = 'package.xml';
  24581. }
  24582. $configureoptions = empty($options['configureoptions']) ? '' : $options['configureoptions'];
  24583. $builder = new PEAR_Builder($configureoptions, $this->ui);
  24584. $this->debug = $this->config->get('verbose');
  24585. $err = $builder->build($params[0], array(&$this, 'buildCallback'));
  24586. if (PEAR::isError($err)) {
  24587. return $err;
  24588. }
  24589. return true;
  24590. }
  24591. function buildCallback($what, $data)
  24592. {
  24593. if (($what == 'cmdoutput' && $this->debug > 1) ||
  24594. ($what == 'output' && $this->debug > 0)) {
  24595. $this->ui->outputData(rtrim($data), 'build');
  24596. }
  24597. }
  24598. }
  24599. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Channels.xml��������������������������������������������������������������0000644�0001750�0001750�00000010172�14720722517�016651� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<commands version="1.0">
  24600. <list-channels>
  24601. <summary>List Available Channels</summary>
  24602. <function>doList</function>
  24603. <shortcut>lc</shortcut>
  24604. <options />
  24605. <doc>
  24606. List all available channels for installation.
  24607. </doc>
  24608. </list-channels>
  24609. <update-channels>
  24610. <summary>Update the Channel List</summary>
  24611. <function>doUpdateAll</function>
  24612. <shortcut>uc</shortcut>
  24613. <options />
  24614. <doc>
  24615. List all installed packages in all channels.
  24616. </doc>
  24617. </update-channels>
  24618. <channel-delete>
  24619. <summary>Remove a Channel From the List</summary>
  24620. <function>doDelete</function>
  24621. <shortcut>cde</shortcut>
  24622. <options />
  24623. <doc>&lt;channel name&gt;
  24624. Delete a channel from the registry. You may not
  24625. remove any channel that has installed packages.
  24626. </doc>
  24627. </channel-delete>
  24628. <channel-add>
  24629. <summary>Add a Channel</summary>
  24630. <function>doAdd</function>
  24631. <shortcut>ca</shortcut>
  24632. <options />
  24633. <doc>&lt;channel.xml&gt;
  24634. Add a private channel to the channel list. Note that all
  24635. public channels should be synced using &quot;update-channels&quot;.
  24636. Parameter may be either a local file or remote URL to a
  24637. channel.xml.
  24638. </doc>
  24639. </channel-add>
  24640. <channel-update>
  24641. <summary>Update an Existing Channel</summary>
  24642. <function>doUpdate</function>
  24643. <shortcut>cu</shortcut>
  24644. <options>
  24645. <force>
  24646. <shortopt>f</shortopt>
  24647. <doc>will force download of new channel.xml if an existing channel name is used</doc>
  24648. </force>
  24649. <channel>
  24650. <shortopt>c</shortopt>
  24651. <doc>will force download of new channel.xml if an existing channel name is used</doc>
  24652. <arg>CHANNEL</arg>
  24653. </channel>
  24654. </options>
  24655. <doc>[&lt;channel.xml&gt;|&lt;channel name&gt;]
  24656. Update a channel in the channel list directly. Note that all
  24657. public channels can be synced using &quot;update-channels&quot;.
  24658. Parameter may be a local or remote channel.xml, or the name of
  24659. an existing channel.
  24660. </doc>
  24661. </channel-update>
  24662. <channel-info>
  24663. <summary>Retrieve Information on a Channel</summary>
  24664. <function>doInfo</function>
  24665. <shortcut>ci</shortcut>
  24666. <options />
  24667. <doc>&lt;package&gt;
  24668. List the files in an installed package.
  24669. </doc>
  24670. </channel-info>
  24671. <channel-alias>
  24672. <summary>Specify an alias to a channel name</summary>
  24673. <function>doAlias</function>
  24674. <shortcut>cha</shortcut>
  24675. <options />
  24676. <doc>&lt;channel&gt; &lt;alias&gt;
  24677. Specify a specific alias to use for a channel name.
  24678. The alias may not be an existing channel name or
  24679. alias.
  24680. </doc>
  24681. </channel-alias>
  24682. <channel-discover>
  24683. <summary>Initialize a Channel from its server</summary>
  24684. <function>doDiscover</function>
  24685. <shortcut>di</shortcut>
  24686. <options />
  24687. <doc>[&lt;channel.xml&gt;|&lt;channel name&gt;]
  24688. Initialize a channel from its server and create a local channel.xml.
  24689. If &lt;channel name&gt; is in the format &quot;&lt;username&gt;:&lt;password&gt;@&lt;channel&gt;&quot; then
  24690. &lt;username&gt; and &lt;password&gt; will be set as the login username/password for
  24691. &lt;channel&gt;. Use caution when passing the username/password in this way, as
  24692. it may allow other users on your computer to briefly view your username/
  24693. password via the system&#039;s process list.
  24694. </doc>
  24695. </channel-discover>
  24696. <channel-login>
  24697. <summary>Connects and authenticates to remote channel server</summary>
  24698. <function>doLogin</function>
  24699. <shortcut>cli</shortcut>
  24700. <options />
  24701. <doc>&lt;channel name&gt;
  24702. Log in to a remote channel server. If &lt;channel name&gt; is not supplied,
  24703. the default channel is used. To use remote functions in the installer
  24704. that require any kind of privileges, you need to log in first. The
  24705. username and password you enter here will be stored in your per-user
  24706. PEAR configuration (~/.pearrc on Unix-like systems). After logging
  24707. in, your username and password will be sent along in subsequent
  24708. operations on the remote server.</doc>
  24709. </channel-login>
  24710. <channel-logout>
  24711. <summary>Logs out from the remote channel server</summary>
  24712. <function>doLogout</function>
  24713. <shortcut>clo</shortcut>
  24714. <options />
  24715. <doc>&lt;channel name&gt;
  24716. Logs out from a remote channel server. If &lt;channel name&gt; is not supplied,
  24717. the default channel is used. This command does not actually connect to the
  24718. remote server, it only deletes the stored username and password from your user
  24719. configuration.</doc>
  24720. </channel-logout>
  24721. </commands>������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Channels.php��������������������������������������������������������������0000664�0001750�0001750�00000101245�14720722517�016644� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  24722. // /* vim: set expandtab tabstop=4 shiftwidth=4: */
  24723. /**
  24724. * PEAR_Command_Channels (list-channels, update-channels, channel-delete, channel-add,
  24725. * channel-update, channel-info, channel-alias, channel-discover commands)
  24726. *
  24727. * PHP versions 4 and 5
  24728. *
  24729. * @category pear
  24730. * @package PEAR
  24731. * @author Stig Bakken <ssb@php.net>
  24732. * @author Greg Beaver <cellog@php.net>
  24733. * @copyright 1997-2009 The Authors
  24734. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  24735. * @link http://pear.php.net/package/PEAR
  24736. * @since File available since Release 1.4.0a1
  24737. */
  24738. /**
  24739. * base class
  24740. */
  24741. require_once 'PEAR/Command/Common.php';
  24742. define('PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS', -500);
  24743. /**
  24744. * PEAR commands for managing channels.
  24745. *
  24746. * @category pear
  24747. * @package PEAR
  24748. * @author Greg Beaver <cellog@php.net>
  24749. * @copyright 1997-2009 The Authors
  24750. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  24751. * @version Release: 1.10.16
  24752. * @link http://pear.php.net/package/PEAR
  24753. * @since Class available since Release 1.4.0a1
  24754. */
  24755. class PEAR_Command_Channels extends PEAR_Command_Common
  24756. {
  24757. var $commands = array(
  24758. 'list-channels' => array(
  24759. 'summary' => 'List Available Channels',
  24760. 'function' => 'doList',
  24761. 'shortcut' => 'lc',
  24762. 'options' => array(),
  24763. 'doc' => '
  24764. List all available channels for installation.
  24765. ',
  24766. ),
  24767. 'update-channels' => array(
  24768. 'summary' => 'Update the Channel List',
  24769. 'function' => 'doUpdateAll',
  24770. 'shortcut' => 'uc',
  24771. 'options' => array(),
  24772. 'doc' => '
  24773. List all installed packages in all channels.
  24774. '
  24775. ),
  24776. 'channel-delete' => array(
  24777. 'summary' => 'Remove a Channel From the List',
  24778. 'function' => 'doDelete',
  24779. 'shortcut' => 'cde',
  24780. 'options' => array(),
  24781. 'doc' => '<channel name>
  24782. Delete a channel from the registry. You may not
  24783. remove any channel that has installed packages.
  24784. '
  24785. ),
  24786. 'channel-add' => array(
  24787. 'summary' => 'Add a Channel',
  24788. 'function' => 'doAdd',
  24789. 'shortcut' => 'ca',
  24790. 'options' => array(),
  24791. 'doc' => '<channel.xml>
  24792. Add a private channel to the channel list. Note that all
  24793. public channels should be synced using "update-channels".
  24794. Parameter may be either a local file or remote URL to a
  24795. channel.xml.
  24796. '
  24797. ),
  24798. 'channel-update' => array(
  24799. 'summary' => 'Update an Existing Channel',
  24800. 'function' => 'doUpdate',
  24801. 'shortcut' => 'cu',
  24802. 'options' => array(
  24803. 'force' => array(
  24804. 'shortopt' => 'f',
  24805. 'doc' => 'will force download of new channel.xml if an existing channel name is used',
  24806. ),
  24807. 'channel' => array(
  24808. 'shortopt' => 'c',
  24809. 'arg' => 'CHANNEL',
  24810. 'doc' => 'will force download of new channel.xml if an existing channel name is used',
  24811. ),
  24812. ),
  24813. 'doc' => '[<channel.xml>|<channel name>]
  24814. Update a channel in the channel list directly. Note that all
  24815. public channels can be synced using "update-channels".
  24816. Parameter may be a local or remote channel.xml, or the name of
  24817. an existing channel.
  24818. '
  24819. ),
  24820. 'channel-info' => array(
  24821. 'summary' => 'Retrieve Information on a Channel',
  24822. 'function' => 'doInfo',
  24823. 'shortcut' => 'ci',
  24824. 'options' => array(),
  24825. 'doc' => '<package>
  24826. List the files in an installed package.
  24827. '
  24828. ),
  24829. 'channel-alias' => array(
  24830. 'summary' => 'Specify an alias to a channel name',
  24831. 'function' => 'doAlias',
  24832. 'shortcut' => 'cha',
  24833. 'options' => array(),
  24834. 'doc' => '<channel> <alias>
  24835. Specify a specific alias to use for a channel name.
  24836. The alias may not be an existing channel name or
  24837. alias.
  24838. '
  24839. ),
  24840. 'channel-discover' => array(
  24841. 'summary' => 'Initialize a Channel from its server',
  24842. 'function' => 'doDiscover',
  24843. 'shortcut' => 'di',
  24844. 'options' => array(),
  24845. 'doc' => '[<channel.xml>|<channel name>]
  24846. Initialize a channel from its server and create a local channel.xml.
  24847. If <channel name> is in the format "<username>:<password>@<channel>" then
  24848. <username> and <password> will be set as the login username/password for
  24849. <channel>. Use caution when passing the username/password in this way, as
  24850. it may allow other users on your computer to briefly view your username/
  24851. password via the system\'s process list.
  24852. '
  24853. ),
  24854. 'channel-login' => array(
  24855. 'summary' => 'Connects and authenticates to remote channel server',
  24856. 'shortcut' => 'cli',
  24857. 'function' => 'doLogin',
  24858. 'options' => array(),
  24859. 'doc' => '<channel name>
  24860. Log in to a remote channel server. If <channel name> is not supplied,
  24861. the default channel is used. To use remote functions in the installer
  24862. that require any kind of privileges, you need to log in first. The
  24863. username and password you enter here will be stored in your per-user
  24864. PEAR configuration (~/.pearrc on Unix-like systems). After logging
  24865. in, your username and password will be sent along in subsequent
  24866. operations on the remote server.',
  24867. ),
  24868. 'channel-logout' => array(
  24869. 'summary' => 'Logs out from the remote channel server',
  24870. 'shortcut' => 'clo',
  24871. 'function' => 'doLogout',
  24872. 'options' => array(),
  24873. 'doc' => '<channel name>
  24874. Logs out from a remote channel server. If <channel name> is not supplied,
  24875. the default channel is used. This command does not actually connect to the
  24876. remote server, it only deletes the stored username and password from your user
  24877. configuration.',
  24878. ),
  24879. );
  24880. /**
  24881. * PEAR_Command_Registry constructor.
  24882. *
  24883. * @access public
  24884. */
  24885. function __construct(&$ui, &$config)
  24886. {
  24887. parent::__construct($ui, $config);
  24888. }
  24889. function _sortChannels($a, $b)
  24890. {
  24891. return strnatcasecmp($a->getName(), $b->getName());
  24892. }
  24893. function doList($command, $options, $params)
  24894. {
  24895. $reg = &$this->config->getRegistry();
  24896. $registered = $reg->getChannels();
  24897. usort($registered, array(&$this, '_sortchannels'));
  24898. $i = $j = 0;
  24899. $data = array(
  24900. 'caption' => 'Registered Channels:',
  24901. 'border' => true,
  24902. 'headline' => array('Channel', 'Alias', 'Summary')
  24903. );
  24904. foreach ($registered as $channel) {
  24905. $data['data'][] = array($channel->getName(),
  24906. $channel->getAlias(),
  24907. $channel->getSummary());
  24908. }
  24909. if (count($registered) === 0) {
  24910. $data = '(no registered channels)';
  24911. }
  24912. $this->ui->outputData($data, $command);
  24913. return true;
  24914. }
  24915. function doUpdateAll($command, $options, $params)
  24916. {
  24917. $reg = &$this->config->getRegistry();
  24918. $channels = $reg->getChannels();
  24919. $success = true;
  24920. foreach ($channels as $channel) {
  24921. if ($channel->getName() != '__uri') {
  24922. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  24923. $err = $this->doUpdate('channel-update',
  24924. $options,
  24925. array($channel->getName()));
  24926. if (PEAR::isError($err)) {
  24927. $this->ui->outputData($err->getMessage(), $command);
  24928. $success = false;
  24929. } else {
  24930. $success &= $err;
  24931. }
  24932. }
  24933. }
  24934. return $success;
  24935. }
  24936. function doInfo($command, $options, $params)
  24937. {
  24938. if (count($params) !== 1) {
  24939. return $this->raiseError("No channel specified");
  24940. }
  24941. $reg = &$this->config->getRegistry();
  24942. $channel = strtolower($params[0]);
  24943. if ($reg->channelExists($channel)) {
  24944. $chan = $reg->getChannel($channel);
  24945. if (PEAR::isError($chan)) {
  24946. return $this->raiseError($chan);
  24947. }
  24948. } else {
  24949. if (strpos($channel, '://')) {
  24950. $downloader = &$this->getDownloader();
  24951. $tmpdir = $this->config->get('temp_dir');
  24952. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  24953. $loc = $downloader->downloadHttp($channel, $this->ui, $tmpdir);
  24954. PEAR::staticPopErrorHandling();
  24955. if (PEAR::isError($loc)) {
  24956. return $this->raiseError('Cannot open "' . $channel .
  24957. '" (' . $loc->getMessage() . ')');
  24958. } else {
  24959. $contents = implode('', file($loc));
  24960. }
  24961. } else {
  24962. if (!file_exists($params[0])) {
  24963. return $this->raiseError('Unknown channel "' . $channel . '"');
  24964. }
  24965. $fp = fopen($params[0], 'r');
  24966. if (!$fp) {
  24967. return $this->raiseError('Cannot open "' . $params[0] . '"');
  24968. }
  24969. $contents = '';
  24970. while (!feof($fp)) {
  24971. $contents .= fread($fp, 1024);
  24972. }
  24973. fclose($fp);
  24974. }
  24975. if (!class_exists('PEAR_ChannelFile')) {
  24976. require_once 'PEAR/ChannelFile.php';
  24977. }
  24978. $chan = new PEAR_ChannelFile;
  24979. $chan->fromXmlString($contents);
  24980. $chan->validate();
  24981. if ($errs = $chan->getErrors(true)) {
  24982. foreach ($errs as $err) {
  24983. $this->ui->outputData($err['level'] . ': ' . $err['message']);
  24984. }
  24985. return $this->raiseError('Channel file "' . $params[0] . '" is not valid');
  24986. }
  24987. }
  24988. if (!$chan) {
  24989. return $this->raiseError('Serious error: Channel "' . $params[0] .
  24990. '" has a corrupted registry entry');
  24991. }
  24992. $channel = $chan->getName();
  24993. $caption = 'Channel ' . $channel . ' Information:';
  24994. $data1 = array(
  24995. 'caption' => $caption,
  24996. 'border' => true);
  24997. $data1['data']['server'] = array('Name and Server', $chan->getName());
  24998. if ($chan->getAlias() != $chan->getName()) {
  24999. $data1['data']['alias'] = array('Alias', $chan->getAlias());
  25000. }
  25001. $data1['data']['summary'] = array('Summary', $chan->getSummary());
  25002. $validate = $chan->getValidationPackage();
  25003. $data1['data']['vpackage'] = array('Validation Package Name', $validate['_content']);
  25004. $data1['data']['vpackageversion'] =
  25005. array('Validation Package Version', $validate['attribs']['version']);
  25006. $d = array();
  25007. $d['main'] = $data1;
  25008. $data['data'] = array();
  25009. $data['caption'] = 'Server Capabilities';
  25010. $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base');
  25011. if ($chan->supportsREST()) {
  25012. if ($chan->supportsREST()) {
  25013. $funcs = $chan->getFunctions('rest');
  25014. if (!isset($funcs[0])) {
  25015. $funcs = array($funcs);
  25016. }
  25017. foreach ($funcs as $protocol) {
  25018. $data['data'][] = array('rest', $protocol['attribs']['type'],
  25019. $protocol['_content']);
  25020. }
  25021. }
  25022. } else {
  25023. $data['data'][] = array('No supported protocols');
  25024. }
  25025. $d['protocols'] = $data;
  25026. $data['data'] = array();
  25027. $mirrors = $chan->getMirrors();
  25028. if ($mirrors) {
  25029. $data['caption'] = 'Channel ' . $channel . ' Mirrors:';
  25030. unset($data['headline']);
  25031. foreach ($mirrors as $mirror) {
  25032. $data['data'][] = array($mirror['attribs']['host']);
  25033. $d['mirrors'] = $data;
  25034. }
  25035. foreach ($mirrors as $i => $mirror) {
  25036. $data['data'] = array();
  25037. $data['caption'] = 'Mirror ' . $mirror['attribs']['host'] . ' Capabilities';
  25038. $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base');
  25039. if ($chan->supportsREST($mirror['attribs']['host'])) {
  25040. if ($chan->supportsREST($mirror['attribs']['host'])) {
  25041. $funcs = $chan->getFunctions('rest', $mirror['attribs']['host']);
  25042. if (!isset($funcs[0])) {
  25043. $funcs = array($funcs);
  25044. }
  25045. foreach ($funcs as $protocol) {
  25046. $data['data'][] = array('rest', $protocol['attribs']['type'],
  25047. $protocol['_content']);
  25048. }
  25049. }
  25050. } else {
  25051. $data['data'][] = array('No supported protocols');
  25052. }
  25053. $d['mirrorprotocols' . $i] = $data;
  25054. }
  25055. }
  25056. $this->ui->outputData($d, 'channel-info');
  25057. }
  25058. // }}}
  25059. function doDelete($command, $options, $params)
  25060. {
  25061. if (count($params) !== 1) {
  25062. return $this->raiseError('channel-delete: no channel specified');
  25063. }
  25064. $reg = &$this->config->getRegistry();
  25065. if (!$reg->channelExists($params[0])) {
  25066. return $this->raiseError('channel-delete: channel "' . $params[0] . '" does not exist');
  25067. }
  25068. $channel = $reg->channelName($params[0]);
  25069. if ($channel == 'pear.php.net') {
  25070. return $this->raiseError('Cannot delete the pear.php.net channel');
  25071. }
  25072. if ($channel == 'pecl.php.net') {
  25073. return $this->raiseError('Cannot delete the pecl.php.net channel');
  25074. }
  25075. if ($channel == 'doc.php.net') {
  25076. return $this->raiseError('Cannot delete the doc.php.net channel');
  25077. }
  25078. if ($channel == '__uri') {
  25079. return $this->raiseError('Cannot delete the __uri pseudo-channel');
  25080. }
  25081. if (PEAR::isError($err = $reg->listPackages($channel))) {
  25082. return $err;
  25083. }
  25084. if (count($err)) {
  25085. return $this->raiseError('Channel "' . $channel .
  25086. '" has installed packages, cannot delete');
  25087. }
  25088. if (!$reg->deleteChannel($channel)) {
  25089. return $this->raiseError('Channel "' . $channel . '" deletion failed');
  25090. } else {
  25091. $this->config->deleteChannel($channel);
  25092. $this->ui->outputData('Channel "' . $channel . '" deleted', $command);
  25093. }
  25094. }
  25095. function doAdd($command, $options, $params)
  25096. {
  25097. if (count($params) !== 1) {
  25098. return $this->raiseError('channel-add: no channel file specified');
  25099. }
  25100. if (strpos($params[0], '://')) {
  25101. $downloader = &$this->getDownloader();
  25102. $tmpdir = $this->config->get('temp_dir');
  25103. if (!file_exists($tmpdir)) {
  25104. require_once 'System.php';
  25105. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  25106. $err = System::mkdir(array('-p', $tmpdir));
  25107. PEAR::staticPopErrorHandling();
  25108. if (PEAR::isError($err)) {
  25109. return $this->raiseError('channel-add: temp_dir does not exist: "' .
  25110. $tmpdir .
  25111. '" - You can change this location with "pear config-set temp_dir"');
  25112. }
  25113. }
  25114. if (!is_writable($tmpdir)) {
  25115. return $this->raiseError('channel-add: temp_dir is not writable: "' .
  25116. $tmpdir .
  25117. '" - You can change this location with "pear config-set temp_dir"');
  25118. }
  25119. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  25120. $loc = $downloader->downloadHttp($params[0], $this->ui, $tmpdir, null, false);
  25121. PEAR::staticPopErrorHandling();
  25122. if (PEAR::isError($loc)) {
  25123. return $this->raiseError('channel-add: Cannot open "' . $params[0] .
  25124. '" (' . $loc->getMessage() . ')');
  25125. }
  25126. list($loc, $lastmodified) = $loc;
  25127. $contents = implode('', file($loc));
  25128. } else {
  25129. $lastmodified = $fp = false;
  25130. if (file_exists($params[0])) {
  25131. $fp = fopen($params[0], 'r');
  25132. }
  25133. if (!$fp) {
  25134. return $this->raiseError('channel-add: cannot open "' . $params[0] . '"');
  25135. }
  25136. $contents = '';
  25137. while (!feof($fp)) {
  25138. $contents .= fread($fp, 1024);
  25139. }
  25140. fclose($fp);
  25141. }
  25142. if (!class_exists('PEAR_ChannelFile')) {
  25143. require_once 'PEAR/ChannelFile.php';
  25144. }
  25145. $channel = new PEAR_ChannelFile;
  25146. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  25147. $result = $channel->fromXmlString($contents);
  25148. PEAR::staticPopErrorHandling();
  25149. if (!$result) {
  25150. $exit = false;
  25151. if (count($errors = $channel->getErrors(true))) {
  25152. foreach ($errors as $error) {
  25153. $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message']));
  25154. if (!$exit) {
  25155. $exit = $error['level'] == 'error' ? true : false;
  25156. }
  25157. }
  25158. if ($exit) {
  25159. return $this->raiseError('channel-add: invalid channel.xml file');
  25160. }
  25161. }
  25162. }
  25163. $reg = &$this->config->getRegistry();
  25164. if ($reg->channelExists($channel->getName())) {
  25165. return $this->raiseError('channel-add: Channel "' . $channel->getName() .
  25166. '" exists, use channel-update to update entry', PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS);
  25167. }
  25168. $ret = $reg->addChannel($channel, $lastmodified);
  25169. if (PEAR::isError($ret)) {
  25170. return $ret;
  25171. }
  25172. if (!$ret) {
  25173. return $this->raiseError('channel-add: adding Channel "' . $channel->getName() .
  25174. '" to registry failed');
  25175. }
  25176. $this->config->setChannels($reg->listChannels());
  25177. $this->config->writeConfigFile();
  25178. $this->ui->outputData('Adding Channel "' . $channel->getName() . '" succeeded', $command);
  25179. }
  25180. function doUpdate($command, $options, $params)
  25181. {
  25182. if (count($params) !== 1) {
  25183. return $this->raiseError("No channel file specified");
  25184. }
  25185. $tmpdir = $this->config->get('temp_dir');
  25186. if (!file_exists($tmpdir)) {
  25187. require_once 'System.php';
  25188. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  25189. $err = System::mkdir(array('-p', $tmpdir));
  25190. PEAR::staticPopErrorHandling();
  25191. if (PEAR::isError($err)) {
  25192. return $this->raiseError('channel-add: temp_dir does not exist: "' .
  25193. $tmpdir .
  25194. '" - You can change this location with "pear config-set temp_dir"');
  25195. }
  25196. }
  25197. if (!is_writable($tmpdir)) {
  25198. return $this->raiseError('channel-add: temp_dir is not writable: "' .
  25199. $tmpdir .
  25200. '" - You can change this location with "pear config-set temp_dir"');
  25201. }
  25202. $reg = &$this->config->getRegistry();
  25203. $lastmodified = false;
  25204. if ((!file_exists($params[0]) || is_dir($params[0]))
  25205. && $reg->channelExists(strtolower($params[0]))) {
  25206. $c = $reg->getChannel(strtolower($params[0]));
  25207. if (PEAR::isError($c)) {
  25208. return $this->raiseError($c);
  25209. }
  25210. $this->ui->outputData("Updating channel \"$params[0]\"", $command);
  25211. $dl = &$this->getDownloader(array());
  25212. // if force is specified, use a timestamp of "1" to force retrieval
  25213. $lastmodified = isset($options['force']) ? false : $c->lastModified();
  25214. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  25215. $contents = $dl->downloadHttp('http://' . $c->getName() . '/channel.xml',
  25216. $this->ui, $tmpdir, null, $lastmodified);
  25217. PEAR::staticPopErrorHandling();
  25218. if (PEAR::isError($contents)) {
  25219. // Attempt to fall back to https
  25220. $this->ui->outputData("Channel \"$params[0]\" is not responding over http://, failed with message: " . $contents->getMessage());
  25221. $this->ui->outputData("Trying channel \"$params[0]\" over https:// instead");
  25222. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  25223. $contents = $dl->downloadHttp('https://' . $c->getName() . '/channel.xml',
  25224. $this->ui, $tmpdir, null, $lastmodified);
  25225. PEAR::staticPopErrorHandling();
  25226. if (PEAR::isError($contents)) {
  25227. return $this->raiseError('Cannot retrieve channel.xml for channel "' .
  25228. $c->getName() . '" (' . $contents->getMessage() . ')');
  25229. }
  25230. }
  25231. list($contents, $lastmodified) = $contents;
  25232. if (!$contents) {
  25233. $this->ui->outputData("Channel \"$params[0]\" is up to date");
  25234. return;
  25235. }
  25236. $contents = implode('', file($contents));
  25237. if (!class_exists('PEAR_ChannelFile')) {
  25238. require_once 'PEAR/ChannelFile.php';
  25239. }
  25240. $channel = new PEAR_ChannelFile;
  25241. $channel->fromXmlString($contents);
  25242. if (!$channel->getErrors()) {
  25243. // security check: is the downloaded file for the channel we got it from?
  25244. if (strtolower($channel->getName()) != strtolower($c->getName())) {
  25245. if (!isset($options['force'])) {
  25246. return $this->raiseError('ERROR: downloaded channel definition file' .
  25247. ' for channel "' . $channel->getName() . '" from channel "' .
  25248. strtolower($c->getName()) . '"');
  25249. }
  25250. $this->ui->log(0, 'WARNING: downloaded channel definition file' .
  25251. ' for channel "' . $channel->getName() . '" from channel "' .
  25252. strtolower($c->getName()) . '"');
  25253. }
  25254. }
  25255. } else {
  25256. if (strpos($params[0], '://')) {
  25257. $dl = &$this->getDownloader();
  25258. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  25259. $loc = $dl->downloadHttp($params[0],
  25260. $this->ui, $tmpdir, null, $lastmodified);
  25261. PEAR::staticPopErrorHandling();
  25262. if (PEAR::isError($loc)) {
  25263. return $this->raiseError("Cannot open " . $params[0] .
  25264. ' (' . $loc->getMessage() . ')');
  25265. }
  25266. list($loc, $lastmodified) = $loc;
  25267. $contents = implode('', file($loc));
  25268. } else {
  25269. $fp = false;
  25270. if (file_exists($params[0])) {
  25271. $fp = fopen($params[0], 'r');
  25272. }
  25273. if (!$fp) {
  25274. return $this->raiseError("Cannot open " . $params[0]);
  25275. }
  25276. $contents = '';
  25277. while (!feof($fp)) {
  25278. $contents .= fread($fp, 1024);
  25279. }
  25280. fclose($fp);
  25281. }
  25282. if (!class_exists('PEAR_ChannelFile')) {
  25283. require_once 'PEAR/ChannelFile.php';
  25284. }
  25285. $channel = new PEAR_ChannelFile;
  25286. $channel->fromXmlString($contents);
  25287. }
  25288. $exit = false;
  25289. if (count($errors = $channel->getErrors(true))) {
  25290. foreach ($errors as $error) {
  25291. $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message']));
  25292. if (!$exit) {
  25293. $exit = $error['level'] == 'error' ? true : false;
  25294. }
  25295. }
  25296. if ($exit) {
  25297. return $this->raiseError('Invalid channel.xml file');
  25298. }
  25299. }
  25300. if (!$reg->channelExists($channel->getName())) {
  25301. return $this->raiseError('Error: Channel "' . $channel->getName() .
  25302. '" does not exist, use channel-add to add an entry');
  25303. }
  25304. $ret = $reg->updateChannel($channel, $lastmodified);
  25305. if (PEAR::isError($ret)) {
  25306. return $ret;
  25307. }
  25308. if (!$ret) {
  25309. return $this->raiseError('Updating Channel "' . $channel->getName() .
  25310. '" in registry failed');
  25311. }
  25312. $this->config->setChannels($reg->listChannels());
  25313. $this->config->writeConfigFile();
  25314. $this->ui->outputData('Update of Channel "' . $channel->getName() . '" succeeded');
  25315. }
  25316. function &getDownloader()
  25317. {
  25318. if (!class_exists('PEAR_Downloader')) {
  25319. require_once 'PEAR/Downloader.php';
  25320. }
  25321. $a = new PEAR_Downloader($this->ui, array(), $this->config);
  25322. return $a;
  25323. }
  25324. function doAlias($command, $options, $params)
  25325. {
  25326. if (count($params) === 1) {
  25327. return $this->raiseError('No channel alias specified');
  25328. }
  25329. if (count($params) !== 2 || (!empty($params[1]) && $params[1][0] == '-')) {
  25330. return $this->raiseError(
  25331. 'Invalid format, correct is: channel-alias channel alias');
  25332. }
  25333. $reg = &$this->config->getRegistry();
  25334. if (!$reg->channelExists($params[0], true)) {
  25335. $extra = '';
  25336. if ($reg->isAlias($params[0])) {
  25337. $extra = ' (use "channel-alias ' . $reg->channelName($params[0]) . ' ' .
  25338. strtolower($params[1]) . '")';
  25339. }
  25340. return $this->raiseError('"' . $params[0] . '" is not a valid channel' . $extra);
  25341. }
  25342. if ($reg->isAlias($params[1])) {
  25343. return $this->raiseError('Channel "' . $reg->channelName($params[1]) . '" is ' .
  25344. 'already aliased to "' . strtolower($params[1]) . '", cannot re-alias');
  25345. }
  25346. $chan = $reg->getChannel($params[0]);
  25347. if (PEAR::isError($chan)) {
  25348. return $this->raiseError('Corrupt registry? Error retrieving channel "' . $params[0] .
  25349. '" information (' . $chan->getMessage() . ')');
  25350. }
  25351. // make it a local alias
  25352. if (!$chan->setAlias(strtolower($params[1]), true)) {
  25353. return $this->raiseError('Alias "' . strtolower($params[1]) .
  25354. '" is not a valid channel alias');
  25355. }
  25356. $reg->updateChannel($chan);
  25357. $this->ui->outputData('Channel "' . $chan->getName() . '" aliased successfully to "' .
  25358. strtolower($params[1]) . '"');
  25359. }
  25360. /**
  25361. * The channel-discover command
  25362. *
  25363. * @param string $command command name
  25364. * @param array $options option_name => value
  25365. * @param array $params list of additional parameters.
  25366. * $params[0] should contain a string with either:
  25367. * - <channel name> or
  25368. * - <username>:<password>@<channel name>
  25369. * @return null|PEAR_Error
  25370. */
  25371. function doDiscover($command, $options, $params)
  25372. {
  25373. if (count($params) !== 1) {
  25374. return $this->raiseError("No channel server specified");
  25375. }
  25376. // Look for the possible input format "<username>:<password>@<channel>"
  25377. if (preg_match('/^(.+):(.+)@(.+)\\z/', $params[0], $matches)) {
  25378. $username = $matches[1];
  25379. $password = $matches[2];
  25380. $channel = $matches[3];
  25381. } else {
  25382. $channel = $params[0];
  25383. }
  25384. $reg = &$this->config->getRegistry();
  25385. if ($reg->channelExists($channel)) {
  25386. if (!$reg->isAlias($channel)) {
  25387. return $this->raiseError("Channel \"$channel\" is already initialized", PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS);
  25388. }
  25389. return $this->raiseError("A channel alias named \"$channel\" " .
  25390. 'already exists, aliasing channel "' . $reg->channelName($channel)
  25391. . '"');
  25392. }
  25393. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  25394. $err = $this->doAdd($command, $options, array('http://' . $channel . '/channel.xml'));
  25395. $this->popErrorHandling();
  25396. if (PEAR::isError($err)) {
  25397. if ($err->getCode() === PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS) {
  25398. return $this->raiseError("Discovery of channel \"$channel\" failed (" .
  25399. $err->getMessage() . ')');
  25400. }
  25401. // Attempt fetch via https
  25402. $this->ui->outputData("Discovering channel $channel over http:// failed with message: " . $err->getMessage());
  25403. $this->ui->outputData("Trying to discover channel $channel over https:// instead");
  25404. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  25405. $err = $this->doAdd($command, $options, array('https://' . $channel . '/channel.xml'));
  25406. $this->popErrorHandling();
  25407. if (PEAR::isError($err)) {
  25408. return $this->raiseError("Discovery of channel \"$channel\" failed (" .
  25409. $err->getMessage() . ')');
  25410. }
  25411. }
  25412. // Store username/password if they were given
  25413. // Arguably we should do a logintest on the channel here, but since
  25414. // that's awkward on a REST-based channel (even "pear login" doesn't
  25415. // do it for those), and XML-RPC is deprecated, it's fairly pointless.
  25416. if (isset($username)) {
  25417. $this->config->set('username', $username, 'user', $channel);
  25418. $this->config->set('password', $password, 'user', $channel);
  25419. $this->config->store();
  25420. $this->ui->outputData("Stored login for channel \"$channel\" using username \"$username\"", $command);
  25421. }
  25422. $this->ui->outputData("Discovery of channel \"$channel\" succeeded", $command);
  25423. }
  25424. /**
  25425. * Execute the 'login' command.
  25426. *
  25427. * @param string $command command name
  25428. * @param array $options option_name => value
  25429. * @param array $params list of additional parameters
  25430. *
  25431. * @return bool TRUE on success or
  25432. * a PEAR error on failure
  25433. *
  25434. * @access public
  25435. */
  25436. function doLogin($command, $options, $params)
  25437. {
  25438. $reg = &$this->config->getRegistry();
  25439. // If a parameter is supplied, use that as the channel to log in to
  25440. $channel = isset($params[0]) ? $params[0] : $this->config->get('default_channel');
  25441. $chan = $reg->getChannel($channel);
  25442. if (PEAR::isError($chan)) {
  25443. return $this->raiseError($chan);
  25444. }
  25445. $server = $this->config->get('preferred_mirror', null, $channel);
  25446. $username = $this->config->get('username', null, $channel);
  25447. if (empty($username)) {
  25448. $username = isset($_ENV['USER']) ? $_ENV['USER'] : null;
  25449. }
  25450. $this->ui->outputData("Logging in to $server.", $command);
  25451. list($username, $password) = $this->ui->userDialog(
  25452. $command,
  25453. array('Username', 'Password'),
  25454. array('text', 'password'),
  25455. array($username, '')
  25456. );
  25457. $username = trim($username);
  25458. $password = trim($password);
  25459. $ourfile = $this->config->getConfFile('user');
  25460. if (!$ourfile) {
  25461. $ourfile = $this->config->getConfFile('system');
  25462. }
  25463. $this->config->set('username', $username, 'user', $channel);
  25464. $this->config->set('password', $password, 'user', $channel);
  25465. if ($chan->supportsREST()) {
  25466. $ok = true;
  25467. }
  25468. if ($ok !== true) {
  25469. return $this->raiseError('Login failed!');
  25470. }
  25471. $this->ui->outputData("Logged in.", $command);
  25472. // avoid changing any temporary settings changed with -d
  25473. $ourconfig = new PEAR_Config($ourfile, $ourfile);
  25474. $ourconfig->set('username', $username, 'user', $channel);
  25475. $ourconfig->set('password', $password, 'user', $channel);
  25476. $ourconfig->store();
  25477. return true;
  25478. }
  25479. /**
  25480. * Execute the 'logout' command.
  25481. *
  25482. * @param string $command command name
  25483. * @param array $options option_name => value
  25484. * @param array $params list of additional parameters
  25485. *
  25486. * @return bool TRUE on success or
  25487. * a PEAR error on failure
  25488. *
  25489. * @access public
  25490. */
  25491. function doLogout($command, $options, $params)
  25492. {
  25493. $reg = &$this->config->getRegistry();
  25494. // If a parameter is supplied, use that as the channel to log in to
  25495. $channel = isset($params[0]) ? $params[0] : $this->config->get('default_channel');
  25496. $chan = $reg->getChannel($channel);
  25497. if (PEAR::isError($chan)) {
  25498. return $this->raiseError($chan);
  25499. }
  25500. $server = $this->config->get('preferred_mirror', null, $channel);
  25501. $this->ui->outputData("Logging out from $server.", $command);
  25502. $this->config->remove('username', 'user', $channel);
  25503. $this->config->remove('password', 'user', $channel);
  25504. $this->config->store();
  25505. return true;
  25506. }
  25507. }
  25508. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Common.php����������������������������������������������������������������0000664�0001750�0001750�00000020026�14720722517�016336� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  25509. /**
  25510. * PEAR_Command_Common base class
  25511. *
  25512. * PHP versions 4 and 5
  25513. *
  25514. * @category pear
  25515. * @package PEAR
  25516. * @author Stig Bakken <ssb@php.net>
  25517. * @author Greg Beaver <cellog@php.net>
  25518. * @copyright 1997-2009 The Authors
  25519. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  25520. * @link http://pear.php.net/package/PEAR
  25521. * @since File available since Release 0.1
  25522. */
  25523. /**
  25524. * base class
  25525. */
  25526. require_once 'PEAR.php';
  25527. /**
  25528. * PEAR commands base class
  25529. *
  25530. * @category pear
  25531. * @package PEAR
  25532. * @author Stig Bakken <ssb@php.net>
  25533. * @author Greg Beaver <cellog@php.net>
  25534. * @copyright 1997-2009 The Authors
  25535. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  25536. * @version Release: 1.10.16
  25537. * @link http://pear.php.net/package/PEAR
  25538. * @since Class available since Release 0.1
  25539. */
  25540. class PEAR_Command_Common extends PEAR
  25541. {
  25542. /**
  25543. * PEAR_Config object used to pass user system and configuration
  25544. * on when executing commands
  25545. *
  25546. * @var PEAR_Config
  25547. */
  25548. var $config;
  25549. /**
  25550. * @var PEAR_Registry
  25551. * @access protected
  25552. */
  25553. var $_registry;
  25554. /**
  25555. * User Interface object, for all interaction with the user.
  25556. * @var object
  25557. */
  25558. var $ui;
  25559. var $_deps_rel_trans = array(
  25560. 'lt' => '<',
  25561. 'le' => '<=',
  25562. 'eq' => '=',
  25563. 'ne' => '!=',
  25564. 'gt' => '>',
  25565. 'ge' => '>=',
  25566. 'has' => '=='
  25567. );
  25568. var $_deps_type_trans = array(
  25569. 'pkg' => 'package',
  25570. 'ext' => 'extension',
  25571. 'php' => 'PHP',
  25572. 'prog' => 'external program',
  25573. 'ldlib' => 'external library for linking',
  25574. 'rtlib' => 'external runtime library',
  25575. 'os' => 'operating system',
  25576. 'websrv' => 'web server',
  25577. 'sapi' => 'SAPI backend'
  25578. );
  25579. /**
  25580. * PEAR_Command_Common constructor.
  25581. *
  25582. * @access public
  25583. */
  25584. function __construct(&$ui, &$config)
  25585. {
  25586. parent::__construct();
  25587. $this->config = &$config;
  25588. $this->ui = &$ui;
  25589. }
  25590. /**
  25591. * Return a list of all the commands defined by this class.
  25592. * @return array list of commands
  25593. * @access public
  25594. */
  25595. function getCommands()
  25596. {
  25597. $ret = array();
  25598. foreach (array_keys($this->commands) as $command) {
  25599. $ret[$command] = $this->commands[$command]['summary'];
  25600. }
  25601. return $ret;
  25602. }
  25603. /**
  25604. * Return a list of all the command shortcuts defined by this class.
  25605. * @return array shortcut => command
  25606. * @access public
  25607. */
  25608. function getShortcuts()
  25609. {
  25610. $ret = array();
  25611. foreach (array_keys($this->commands) as $command) {
  25612. if (isset($this->commands[$command]['shortcut'])) {
  25613. $ret[$this->commands[$command]['shortcut']] = $command;
  25614. }
  25615. }
  25616. return $ret;
  25617. }
  25618. function getOptions($command)
  25619. {
  25620. $shortcuts = $this->getShortcuts();
  25621. if (isset($shortcuts[$command])) {
  25622. $command = $shortcuts[$command];
  25623. }
  25624. if (isset($this->commands[$command]) &&
  25625. isset($this->commands[$command]['options'])) {
  25626. return $this->commands[$command]['options'];
  25627. }
  25628. return null;
  25629. }
  25630. function getGetoptArgs($command, &$short_args, &$long_args)
  25631. {
  25632. $short_args = '';
  25633. $long_args = array();
  25634. if (empty($this->commands[$command]) || empty($this->commands[$command]['options'])) {
  25635. return;
  25636. }
  25637. reset($this->commands[$command]['options']);
  25638. foreach ($this->commands[$command]['options'] as $option => $info) {
  25639. $larg = $sarg = '';
  25640. if (isset($info['arg'])) {
  25641. if ($info['arg'][0] == '(') {
  25642. $larg = '==';
  25643. $sarg = '::';
  25644. $arg = substr($info['arg'], 1, -1);
  25645. } else {
  25646. $larg = '=';
  25647. $sarg = ':';
  25648. $arg = $info['arg'];
  25649. }
  25650. }
  25651. if (isset($info['shortopt'])) {
  25652. $short_args .= $info['shortopt'] . $sarg;
  25653. }
  25654. $long_args[] = $option . $larg;
  25655. }
  25656. }
  25657. /**
  25658. * Returns the help message for the given command
  25659. *
  25660. * @param string $command The command
  25661. * @return mixed A fail string if the command does not have help or
  25662. * a two elements array containing [0]=>help string,
  25663. * [1]=> help string for the accepted cmd args
  25664. */
  25665. function getHelp($command)
  25666. {
  25667. $config = &PEAR_Config::singleton();
  25668. if (!isset($this->commands[$command])) {
  25669. return "No such command \"$command\"";
  25670. }
  25671. $help = null;
  25672. if (isset($this->commands[$command]['doc'])) {
  25673. $help = $this->commands[$command]['doc'];
  25674. }
  25675. if (empty($help)) {
  25676. // XXX (cox) Fallback to summary if there is no doc (show both?)
  25677. if (!isset($this->commands[$command]['summary'])) {
  25678. return "No help for command \"$command\"";
  25679. }
  25680. $help = $this->commands[$command]['summary'];
  25681. }
  25682. if (preg_match_all('/{config\s+([^\}]+)}/', $help, $matches)) {
  25683. foreach($matches[0] as $k => $v) {
  25684. $help = preg_replace("/$v/", $config->get($matches[1][$k]), $help);
  25685. }
  25686. }
  25687. return array($help, $this->getHelpArgs($command));
  25688. }
  25689. /**
  25690. * Returns the help for the accepted arguments of a command
  25691. *
  25692. * @param string $command
  25693. * @return string The help string
  25694. */
  25695. function getHelpArgs($command)
  25696. {
  25697. if (isset($this->commands[$command]['options']) &&
  25698. count($this->commands[$command]['options']))
  25699. {
  25700. $help = "Options:\n";
  25701. foreach ($this->commands[$command]['options'] as $k => $v) {
  25702. if (isset($v['arg'])) {
  25703. if ($v['arg'][0] == '(') {
  25704. $arg = substr($v['arg'], 1, -1);
  25705. $sapp = " [$arg]";
  25706. $lapp = "[=$arg]";
  25707. } else {
  25708. $sapp = " $v[arg]";
  25709. $lapp = "=$v[arg]";
  25710. }
  25711. } else {
  25712. $sapp = $lapp = "";
  25713. }
  25714. if (isset($v['shortopt'])) {
  25715. $s = $v['shortopt'];
  25716. $help .= " -$s$sapp, --$k$lapp\n";
  25717. } else {
  25718. $help .= " --$k$lapp\n";
  25719. }
  25720. $p = " ";
  25721. $doc = rtrim(str_replace("\n", "\n$p", $v['doc']));
  25722. $help .= " $doc\n";
  25723. }
  25724. return $help;
  25725. }
  25726. return null;
  25727. }
  25728. function run($command, $options, $params)
  25729. {
  25730. if (empty($this->commands[$command]['function'])) {
  25731. // look for shortcuts
  25732. foreach (array_keys($this->commands) as $cmd) {
  25733. if (isset($this->commands[$cmd]['shortcut']) && $this->commands[$cmd]['shortcut'] == $command) {
  25734. if (empty($this->commands[$cmd]['function'])) {
  25735. return $this->raiseError("unknown command `$command'");
  25736. } else {
  25737. $func = $this->commands[$cmd]['function'];
  25738. }
  25739. $command = $cmd;
  25740. //$command = $this->commands[$cmd]['function'];
  25741. break;
  25742. }
  25743. }
  25744. } else {
  25745. $func = $this->commands[$command]['function'];
  25746. }
  25747. return $this->$func($command, $options, $params);
  25748. }
  25749. }
  25750. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Config.xml����������������������������������������������������������������0000644�0001750�0001750�00000006466�14720722517�016336� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<commands version="1.0">
  25751. <config-show>
  25752. <summary>Show All Settings</summary>
  25753. <function>doConfigShow</function>
  25754. <shortcut>csh</shortcut>
  25755. <options>
  25756. <channel>
  25757. <shortopt>c</shortopt>
  25758. <doc>show configuration variables for another channel</doc>
  25759. <arg>CHAN</arg>
  25760. </channel>
  25761. </options>
  25762. <doc>[layer]
  25763. Displays all configuration values. An optional argument
  25764. may be used to tell which configuration layer to display. Valid
  25765. configuration layers are &quot;user&quot;, &quot;system&quot; and &quot;default&quot;. To display
  25766. configurations for different channels, set the default_channel
  25767. configuration variable and run config-show again.
  25768. </doc>
  25769. </config-show>
  25770. <config-get>
  25771. <summary>Show One Setting</summary>
  25772. <function>doConfigGet</function>
  25773. <shortcut>cg</shortcut>
  25774. <options>
  25775. <channel>
  25776. <shortopt>c</shortopt>
  25777. <doc>show configuration variables for another channel</doc>
  25778. <arg>CHAN</arg>
  25779. </channel>
  25780. </options>
  25781. <doc>&lt;parameter&gt; [layer]
  25782. Displays the value of one configuration parameter. The
  25783. first argument is the name of the parameter, an optional second argument
  25784. may be used to tell which configuration layer to look in. Valid configuration
  25785. layers are &quot;user&quot;, &quot;system&quot; and &quot;default&quot;. If no layer is specified, a value
  25786. will be picked from the first layer that defines the parameter, in the order
  25787. just specified. The configuration value will be retrieved for the channel
  25788. specified by the default_channel configuration variable.
  25789. </doc>
  25790. </config-get>
  25791. <config-set>
  25792. <summary>Change Setting</summary>
  25793. <function>doConfigSet</function>
  25794. <shortcut>cs</shortcut>
  25795. <options>
  25796. <channel>
  25797. <shortopt>c</shortopt>
  25798. <doc>show configuration variables for another channel</doc>
  25799. <arg>CHAN</arg>
  25800. </channel>
  25801. </options>
  25802. <doc>&lt;parameter&gt; &lt;value&gt; [layer]
  25803. Sets the value of one configuration parameter. The first argument is
  25804. the name of the parameter, the second argument is the new value. Some
  25805. parameters are subject to validation, and the command will fail with
  25806. an error message if the new value does not make sense. An optional
  25807. third argument may be used to specify in which layer to set the
  25808. configuration parameter. The default layer is &quot;user&quot;. The
  25809. configuration value will be set for the current channel, which
  25810. is controlled by the default_channel configuration variable.
  25811. </doc>
  25812. </config-set>
  25813. <config-help>
  25814. <summary>Show Information About Setting</summary>
  25815. <function>doConfigHelp</function>
  25816. <shortcut>ch</shortcut>
  25817. <options />
  25818. <doc>[parameter]
  25819. Displays help for a configuration parameter. Without arguments it
  25820. displays help for all configuration parameters.
  25821. </doc>
  25822. </config-help>
  25823. <config-create>
  25824. <summary>Create a Default configuration file</summary>
  25825. <function>doConfigCreate</function>
  25826. <shortcut>coc</shortcut>
  25827. <options>
  25828. <windows>
  25829. <shortopt>w</shortopt>
  25830. <doc>create a config file for a windows install</doc>
  25831. </windows>
  25832. </options>
  25833. <doc>&lt;root path&gt; &lt;filename&gt;
  25834. Create a default configuration file with all directory configuration
  25835. variables set to subdirectories of &lt;root path&gt;, and save it as &lt;filename&gt;.
  25836. This is useful especially for creating a configuration file for a remote
  25837. PEAR installation (using the --remoteconfig option of install, upgrade,
  25838. and uninstall).
  25839. </doc>
  25840. </config-create>
  25841. </commands>����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Config.php����������������������������������������������������������������0000664�0001750�0001750�00000036153�14720722517�016323� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  25842. /**
  25843. * PEAR_Command_Config (config-show, config-get, config-set, config-help, config-create commands)
  25844. *
  25845. * PHP versions 4 and 5
  25846. *
  25847. * @category pear
  25848. * @package PEAR
  25849. * @author Stig Bakken <ssb@php.net>
  25850. * @author Greg Beaver <cellog@php.net>
  25851. * @copyright 1997-2009 The Authors
  25852. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  25853. * @link http://pear.php.net/package/PEAR
  25854. * @since File available since Release 0.1
  25855. */
  25856. /**
  25857. * base class
  25858. */
  25859. require_once 'PEAR/Command/Common.php';
  25860. /**
  25861. * PEAR commands for managing configuration data.
  25862. *
  25863. * @category pear
  25864. * @package PEAR
  25865. * @author Stig Bakken <ssb@php.net>
  25866. * @author Greg Beaver <cellog@php.net>
  25867. * @copyright 1997-2009 The Authors
  25868. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  25869. * @version Release: 1.10.16
  25870. * @link http://pear.php.net/package/PEAR
  25871. * @since Class available since Release 0.1
  25872. */
  25873. class PEAR_Command_Config extends PEAR_Command_Common
  25874. {
  25875. var $commands = array(
  25876. 'config-show' => array(
  25877. 'summary' => 'Show All Settings',
  25878. 'function' => 'doConfigShow',
  25879. 'shortcut' => 'csh',
  25880. 'options' => array(
  25881. 'channel' => array(
  25882. 'shortopt' => 'c',
  25883. 'doc' => 'show configuration variables for another channel',
  25884. 'arg' => 'CHAN',
  25885. ),
  25886. ),
  25887. 'doc' => '[layer]
  25888. Displays all configuration values. An optional argument
  25889. may be used to tell which configuration layer to display. Valid
  25890. configuration layers are "user", "system" and "default". To display
  25891. configurations for different channels, set the default_channel
  25892. configuration variable and run config-show again.
  25893. ',
  25894. ),
  25895. 'config-get' => array(
  25896. 'summary' => 'Show One Setting',
  25897. 'function' => 'doConfigGet',
  25898. 'shortcut' => 'cg',
  25899. 'options' => array(
  25900. 'channel' => array(
  25901. 'shortopt' => 'c',
  25902. 'doc' => 'show configuration variables for another channel',
  25903. 'arg' => 'CHAN',
  25904. ),
  25905. ),
  25906. 'doc' => '<parameter> [layer]
  25907. Displays the value of one configuration parameter. The
  25908. first argument is the name of the parameter, an optional second argument
  25909. may be used to tell which configuration layer to look in. Valid configuration
  25910. layers are "user", "system" and "default". If no layer is specified, a value
  25911. will be picked from the first layer that defines the parameter, in the order
  25912. just specified. The configuration value will be retrieved for the channel
  25913. specified by the default_channel configuration variable.
  25914. ',
  25915. ),
  25916. 'config-set' => array(
  25917. 'summary' => 'Change Setting',
  25918. 'function' => 'doConfigSet',
  25919. 'shortcut' => 'cs',
  25920. 'options' => array(
  25921. 'channel' => array(
  25922. 'shortopt' => 'c',
  25923. 'doc' => 'show configuration variables for another channel',
  25924. 'arg' => 'CHAN',
  25925. ),
  25926. ),
  25927. 'doc' => '<parameter> <value> [layer]
  25928. Sets the value of one configuration parameter. The first argument is
  25929. the name of the parameter, the second argument is the new value. Some
  25930. parameters are subject to validation, and the command will fail with
  25931. an error message if the new value does not make sense. An optional
  25932. third argument may be used to specify in which layer to set the
  25933. configuration parameter. The default layer is "user". The
  25934. configuration value will be set for the current channel, which
  25935. is controlled by the default_channel configuration variable.
  25936. ',
  25937. ),
  25938. 'config-help' => array(
  25939. 'summary' => 'Show Information About Setting',
  25940. 'function' => 'doConfigHelp',
  25941. 'shortcut' => 'ch',
  25942. 'options' => array(),
  25943. 'doc' => '[parameter]
  25944. Displays help for a configuration parameter. Without arguments it
  25945. displays help for all configuration parameters.
  25946. ',
  25947. ),
  25948. 'config-create' => array(
  25949. 'summary' => 'Create a Default configuration file',
  25950. 'function' => 'doConfigCreate',
  25951. 'shortcut' => 'coc',
  25952. 'options' => array(
  25953. 'windows' => array(
  25954. 'shortopt' => 'w',
  25955. 'doc' => 'create a config file for a windows install',
  25956. ),
  25957. ),
  25958. 'doc' => '<root path> <filename>
  25959. Create a default configuration file with all directory configuration
  25960. variables set to subdirectories of <root path>, and save it as <filename>.
  25961. This is useful especially for creating a configuration file for a remote
  25962. PEAR installation (using the --remoteconfig option of install, upgrade,
  25963. and uninstall).
  25964. ',
  25965. ),
  25966. );
  25967. /**
  25968. * PEAR_Command_Config constructor.
  25969. *
  25970. * @access public
  25971. */
  25972. function __construct(&$ui, &$config)
  25973. {
  25974. parent::__construct($ui, $config);
  25975. }
  25976. function doConfigShow($command, $options, $params)
  25977. {
  25978. $layer = null;
  25979. if (is_array($params)) {
  25980. $layer = isset($params[0]) ? $params[0] : null;
  25981. }
  25982. // $params[0] -> the layer
  25983. if ($error = $this->_checkLayer($layer)) {
  25984. return $this->raiseError("config-show:$error");
  25985. }
  25986. $keys = $this->config->getKeys();
  25987. sort($keys);
  25988. $channel = isset($options['channel']) ? $options['channel'] :
  25989. $this->config->get('default_channel');
  25990. $reg = &$this->config->getRegistry();
  25991. if (!$reg->channelExists($channel)) {
  25992. return $this->raiseError('Channel "' . $channel . '" does not exist');
  25993. }
  25994. $channel = $reg->channelName($channel);
  25995. $data = array('caption' => 'Configuration (channel ' . $channel . '):');
  25996. foreach ($keys as $key) {
  25997. $type = $this->config->getType($key);
  25998. $value = $this->config->get($key, $layer, $channel);
  25999. if ($type == 'password' && $value) {
  26000. $value = '********';
  26001. }
  26002. if ($value === false) {
  26003. $value = 'false';
  26004. } elseif ($value === true) {
  26005. $value = 'true';
  26006. }
  26007. $data['data'][$this->config->getGroup($key)][] = array($this->config->getPrompt($key) , $key, $value);
  26008. }
  26009. foreach ($this->config->getLayers() as $layer) {
  26010. $data['data']['Config Files'][] = array(ucfirst($layer) . ' Configuration File', 'Filename' , $this->config->getConfFile($layer));
  26011. }
  26012. $this->ui->outputData($data, $command);
  26013. return true;
  26014. }
  26015. function doConfigGet($command, $options, $params)
  26016. {
  26017. $args_cnt = is_array($params) ? count($params) : 0;
  26018. switch ($args_cnt) {
  26019. case 1:
  26020. $config_key = $params[0];
  26021. $layer = null;
  26022. break;
  26023. case 2:
  26024. $config_key = $params[0];
  26025. $layer = $params[1];
  26026. if ($error = $this->_checkLayer($layer)) {
  26027. return $this->raiseError("config-get:$error");
  26028. }
  26029. break;
  26030. case 0:
  26031. default:
  26032. return $this->raiseError("config-get expects 1 or 2 parameters");
  26033. }
  26034. $reg = &$this->config->getRegistry();
  26035. $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel');
  26036. if (!$reg->channelExists($channel)) {
  26037. return $this->raiseError('Channel "' . $channel . '" does not exist');
  26038. }
  26039. $channel = $reg->channelName($channel);
  26040. $this->ui->outputData($this->config->get($config_key, $layer, $channel), $command);
  26041. return true;
  26042. }
  26043. function doConfigSet($command, $options, $params)
  26044. {
  26045. // $param[0] -> a parameter to set
  26046. // $param[1] -> the value for the parameter
  26047. // $param[2] -> the layer
  26048. $failmsg = '';
  26049. if (count($params) < 2 || count($params) > 3) {
  26050. $failmsg .= "config-set expects 2 or 3 parameters";
  26051. return PEAR::raiseError($failmsg);
  26052. }
  26053. if (isset($params[2]) && ($error = $this->_checkLayer($params[2]))) {
  26054. $failmsg .= $error;
  26055. return PEAR::raiseError("config-set:$failmsg");
  26056. }
  26057. $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel');
  26058. $reg = &$this->config->getRegistry();
  26059. if (!$reg->channelExists($channel)) {
  26060. return $this->raiseError('Channel "' . $channel . '" does not exist');
  26061. }
  26062. $channel = $reg->channelName($channel);
  26063. if ($params[0] == 'default_channel' && !$reg->channelExists($params[1])) {
  26064. return $this->raiseError('Channel "' . $params[1] . '" does not exist');
  26065. }
  26066. if ($params[0] == 'preferred_mirror'
  26067. && (
  26068. !$reg->mirrorExists($channel, $params[1]) &&
  26069. (!$reg->channelExists($params[1]) || $channel != $params[1])
  26070. )
  26071. ) {
  26072. $msg = 'Channel Mirror "' . $params[1] . '" does not exist';
  26073. $msg .= ' in your registry for channel "' . $channel . '".';
  26074. $msg .= "\n" . 'Attempt to run "pear channel-update ' . $channel .'"';
  26075. $msg .= ' if you believe this mirror should exist as you may';
  26076. $msg .= ' have outdated channel information.';
  26077. return $this->raiseError($msg);
  26078. }
  26079. if (count($params) == 2) {
  26080. array_push($params, 'user');
  26081. $layer = 'user';
  26082. } else {
  26083. $layer = $params[2];
  26084. }
  26085. array_push($params, $channel);
  26086. if (!call_user_func_array(array(&$this->config, 'set'), $params)) {
  26087. array_pop($params);
  26088. $failmsg = "config-set (" . implode(", ", $params) . ") failed, channel $channel";
  26089. } else {
  26090. $this->config->store($layer);
  26091. }
  26092. if ($failmsg) {
  26093. return $this->raiseError($failmsg);
  26094. }
  26095. $this->ui->outputData('config-set succeeded', $command);
  26096. return true;
  26097. }
  26098. function doConfigHelp($command, $options, $params)
  26099. {
  26100. if (empty($params)) {
  26101. $params = $this->config->getKeys();
  26102. }
  26103. $data['caption'] = "Config help" . ((count($params) == 1) ? " for $params[0]" : '');
  26104. $data['headline'] = array('Name', 'Type', 'Description');
  26105. $data['border'] = true;
  26106. foreach ($params as $name) {
  26107. $type = $this->config->getType($name);
  26108. $docs = $this->config->getDocs($name);
  26109. if ($type == 'set') {
  26110. $docs = rtrim($docs) . "\nValid set: " .
  26111. implode(' ', $this->config->getSetValues($name));
  26112. }
  26113. $data['data'][] = array($name, $type, $docs);
  26114. }
  26115. $this->ui->outputData($data, $command);
  26116. }
  26117. function doConfigCreate($command, $options, $params)
  26118. {
  26119. if (count($params) != 2) {
  26120. return PEAR::raiseError('config-create: must have 2 parameters, root path and ' .
  26121. 'filename to save as');
  26122. }
  26123. $root = $params[0];
  26124. // Clean up the DIRECTORY_SEPARATOR mess
  26125. $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
  26126. $root = preg_replace(array('!\\\\+!', '!/+!', "!$ds2+!"),
  26127. array('/', '/', '/'),
  26128. $root);
  26129. if ($root[0] != '/') {
  26130. if (!isset($options['windows'])) {
  26131. return PEAR::raiseError('Root directory must be an absolute path beginning ' .
  26132. 'with "/", was: "' . $root . '"');
  26133. }
  26134. if (!preg_match('/^[A-Za-z]:/', $root)) {
  26135. return PEAR::raiseError('Root directory must be an absolute path beginning ' .
  26136. 'with "\\" or "C:\\", was: "' . $root . '"');
  26137. }
  26138. }
  26139. $windows = isset($options['windows']);
  26140. if ($windows) {
  26141. $root = str_replace('/', '\\', $root);
  26142. }
  26143. if (!file_exists($params[1]) && !@touch($params[1])) {
  26144. return PEAR::raiseError('Could not create "' . $params[1] . '"');
  26145. }
  26146. $params[1] = realpath($params[1]);
  26147. $config = new PEAR_Config($params[1], '#no#system#config#', false, false);
  26148. if ($root[strlen($root) - 1] == '/') {
  26149. $root = substr($root, 0, strlen($root) - 1);
  26150. }
  26151. $config->noRegistry();
  26152. $config->set('php_dir', $windows ? "$root\\pear\\php" : "$root/pear/php", 'user');
  26153. $config->set('data_dir', $windows ? "$root\\pear\\data" : "$root/pear/data");
  26154. $config->set('www_dir', $windows ? "$root\\pear\\www" : "$root/pear/www");
  26155. $config->set('cfg_dir', $windows ? "$root\\pear\\cfg" : "$root/pear/cfg");
  26156. $config->set('ext_dir', $windows ? "$root\\pear\\ext" : "$root/pear/ext");
  26157. $config->set('doc_dir', $windows ? "$root\\pear\\docs" : "$root/pear/docs");
  26158. $config->set('test_dir', $windows ? "$root\\pear\\tests" : "$root/pear/tests");
  26159. $config->set('cache_dir', $windows ? "$root\\pear\\cache" : "$root/pear/cache");
  26160. $config->set('download_dir', $windows ? "$root\\pear\\download" : "$root/pear/download");
  26161. $config->set('temp_dir', $windows ? "$root\\pear\\temp" : "$root/pear/temp");
  26162. $config->set('bin_dir', $windows ? "$root\\pear" : "$root/pear");
  26163. $config->set('man_dir', $windows ? "$root\\pear\\man" : "$root/pear/man");
  26164. $config->writeConfigFile();
  26165. $this->_showConfig($config);
  26166. $this->ui->outputData('Successfully created default configuration file "' . $params[1] . '"',
  26167. $command);
  26168. }
  26169. function _showConfig(&$config)
  26170. {
  26171. $params = array('user');
  26172. $keys = $config->getKeys();
  26173. sort($keys);
  26174. $channel = 'pear.php.net';
  26175. $data = array('caption' => 'Configuration (channel ' . $channel . '):');
  26176. foreach ($keys as $key) {
  26177. $type = $config->getType($key);
  26178. $value = $config->get($key, 'user', $channel);
  26179. if ($type == 'password' && $value) {
  26180. $value = '********';
  26181. }
  26182. if ($value === false) {
  26183. $value = 'false';
  26184. } elseif ($value === true) {
  26185. $value = 'true';
  26186. }
  26187. $data['data'][$config->getGroup($key)][] =
  26188. array($config->getPrompt($key) , $key, $value);
  26189. }
  26190. foreach ($config->getLayers() as $layer) {
  26191. $data['data']['Config Files'][] =
  26192. array(ucfirst($layer) . ' Configuration File', 'Filename' ,
  26193. $config->getConfFile($layer));
  26194. }
  26195. $this->ui->outputData($data, 'config-show');
  26196. return true;
  26197. }
  26198. /**
  26199. * Checks if a layer is defined or not
  26200. *
  26201. * @param string $layer The layer to search for
  26202. * @return mixed False on no error or the error message
  26203. */
  26204. function _checkLayer($layer = null)
  26205. {
  26206. if (!empty($layer) && $layer != 'default') {
  26207. $layers = $this->config->getLayers();
  26208. if (!in_array($layer, $layers)) {
  26209. return " only the layers: \"" . implode('" or "', $layers) . "\" are supported";
  26210. }
  26211. }
  26212. return false;
  26213. }
  26214. }
  26215. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Install.xml���������������������������������������������������������������0000664�0001750�0001750�00000020763�14720722517�016535� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<commands version="1.0">
  26216. <install>
  26217. <summary>Install Package</summary>
  26218. <function>doInstall</function>
  26219. <shortcut>i</shortcut>
  26220. <options>
  26221. <force>
  26222. <shortopt>f</shortopt>
  26223. <doc>will overwrite newer installed packages</doc>
  26224. </force>
  26225. <loose>
  26226. <shortopt>l</shortopt>
  26227. <doc>do not check for recommended dependency version</doc>
  26228. </loose>
  26229. <nodeps>
  26230. <shortopt>n</shortopt>
  26231. <doc>ignore dependencies, install anyway</doc>
  26232. </nodeps>
  26233. <register-only>
  26234. <shortopt>r</shortopt>
  26235. <doc>do not install files, only register the package as installed</doc>
  26236. </register-only>
  26237. <soft>
  26238. <shortopt>s</shortopt>
  26239. <doc>soft install, fail silently, or upgrade if already installed</doc>
  26240. </soft>
  26241. <nobuild>
  26242. <shortopt>B</shortopt>
  26243. <doc>don&#039;t build C extensions</doc>
  26244. </nobuild>
  26245. <configureoptions>
  26246. <shortopt>D</shortopt>
  26247. <arg>OPTION1=VALUE[ OPTION2=VALUE]</arg>
  26248. </configureoptions>
  26249. <nocompress>
  26250. <shortopt>Z</shortopt>
  26251. <doc>request uncompressed files when downloading</doc>
  26252. </nocompress>
  26253. <installroot>
  26254. <shortopt>R</shortopt>
  26255. <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT), use packagingroot for RPM</doc>
  26256. <arg>DIR</arg>
  26257. </installroot>
  26258. <packagingroot>
  26259. <shortopt>P</shortopt>
  26260. <doc>root directory used when packaging files, like RPM packaging</doc>
  26261. <arg>DIR</arg>
  26262. </packagingroot>
  26263. <ignore-errors>
  26264. <shortopt></shortopt>
  26265. <doc>force install even if there were errors</doc>
  26266. </ignore-errors>
  26267. <alldeps>
  26268. <shortopt>a</shortopt>
  26269. <doc>install all required and optional dependencies</doc>
  26270. </alldeps>
  26271. <onlyreqdeps>
  26272. <shortopt>o</shortopt>
  26273. <doc>install all required dependencies</doc>
  26274. </onlyreqdeps>
  26275. <offline>
  26276. <shortopt>O</shortopt>
  26277. <doc>do not attempt to download any urls or contact channels</doc>
  26278. </offline>
  26279. <pretend>
  26280. <shortopt>p</shortopt>
  26281. <doc>Only list the packages that would be downloaded</doc>
  26282. </pretend>
  26283. </options>
  26284. <doc>[channel/]&lt;package&gt; ...
  26285. Installs one or more PEAR packages. You can specify a package to
  26286. install in four ways:
  26287. &quot;Package-1.0.tgz&quot; : installs from a local file
  26288. &quot;http://example.com/Package-1.0.tgz&quot; : installs from
  26289. anywhere on the net.
  26290. &quot;package.xml&quot; : installs the package described in
  26291. package.xml. Useful for testing, or for wrapping a PEAR package in
  26292. another package manager such as RPM.
  26293. &quot;Package[-version/state][.tar]&quot; : queries your default channel&#039;s server
  26294. ({config master_server}) and downloads the newest package with
  26295. the preferred quality/state ({config preferred_state}).
  26296. To retrieve Package version 1.1, use &quot;Package-1.1,&quot; to retrieve
  26297. Package state beta, use &quot;Package-beta.&quot; To retrieve an uncompressed
  26298. file, append .tar (make sure there is no file by the same name first)
  26299. To download a package from another channel, prefix with the channel name like
  26300. &quot;channel/Package&quot;
  26301. More than one package may be specified at once. It is ok to mix these
  26302. four ways of specifying packages.
  26303. </doc>
  26304. </install>
  26305. <upgrade>
  26306. <summary>Upgrade Package</summary>
  26307. <function>doInstall</function>
  26308. <shortcut>up</shortcut>
  26309. <options>
  26310. <channel>
  26311. <shortopt>c</shortopt>
  26312. <doc>upgrade packages from a specific channel</doc>
  26313. <arg>CHAN</arg>
  26314. </channel>
  26315. <force>
  26316. <shortopt>f</shortopt>
  26317. <doc>overwrite newer installed packages</doc>
  26318. </force>
  26319. <loose>
  26320. <shortopt>l</shortopt>
  26321. <doc>do not check for recommended dependency version</doc>
  26322. </loose>
  26323. <nodeps>
  26324. <shortopt>n</shortopt>
  26325. <doc>ignore dependencies, upgrade anyway</doc>
  26326. </nodeps>
  26327. <register-only>
  26328. <shortopt>r</shortopt>
  26329. <doc>do not install files, only register the package as upgraded</doc>
  26330. </register-only>
  26331. <nobuild>
  26332. <shortopt>B</shortopt>
  26333. <doc>don&#039;t build C extensions</doc>
  26334. </nobuild>
  26335. <nocompress>
  26336. <shortopt>Z</shortopt>
  26337. <doc>request uncompressed files when downloading</doc>
  26338. </nocompress>
  26339. <installroot>
  26340. <shortopt>R</shortopt>
  26341. <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT)</doc>
  26342. <arg>DIR</arg>
  26343. </installroot>
  26344. <ignore-errors>
  26345. <shortopt></shortopt>
  26346. <doc>force install even if there were errors</doc>
  26347. </ignore-errors>
  26348. <alldeps>
  26349. <shortopt>a</shortopt>
  26350. <doc>install all required and optional dependencies</doc>
  26351. </alldeps>
  26352. <onlyreqdeps>
  26353. <shortopt>o</shortopt>
  26354. <doc>install all required dependencies</doc>
  26355. </onlyreqdeps>
  26356. <offline>
  26357. <shortopt>O</shortopt>
  26358. <doc>do not attempt to download any urls or contact channels</doc>
  26359. </offline>
  26360. <pretend>
  26361. <shortopt>p</shortopt>
  26362. <doc>Only list the packages that would be downloaded</doc>
  26363. </pretend>
  26364. </options>
  26365. <doc>&lt;package&gt; ...
  26366. Upgrades one or more PEAR packages. See documentation for the
  26367. &quot;install&quot; command for ways to specify a package.
  26368. When upgrading, your package will be updated if the provided new
  26369. package has a higher version number (use the -f option if you need to
  26370. upgrade anyway).
  26371. More than one package may be specified at once.
  26372. </doc>
  26373. </upgrade>
  26374. <upgrade-all>
  26375. <summary>Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters]</summary>
  26376. <function>doUpgradeAll</function>
  26377. <shortcut>ua</shortcut>
  26378. <options>
  26379. <channel>
  26380. <shortopt>c</shortopt>
  26381. <doc>upgrade packages from a specific channel</doc>
  26382. <arg>CHAN</arg>
  26383. </channel>
  26384. <nodeps>
  26385. <shortopt>n</shortopt>
  26386. <doc>ignore dependencies, upgrade anyway</doc>
  26387. </nodeps>
  26388. <register-only>
  26389. <shortopt>r</shortopt>
  26390. <doc>do not install files, only register the package as upgraded</doc>
  26391. </register-only>
  26392. <nobuild>
  26393. <shortopt>B</shortopt>
  26394. <doc>don&#039;t build C extensions</doc>
  26395. </nobuild>
  26396. <nocompress>
  26397. <shortopt>Z</shortopt>
  26398. <doc>request uncompressed files when downloading</doc>
  26399. </nocompress>
  26400. <installroot>
  26401. <shortopt>R</shortopt>
  26402. <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT), use packagingroot for RPM</doc>
  26403. <arg>DIR</arg>
  26404. </installroot>
  26405. <ignore-errors>
  26406. <shortopt></shortopt>
  26407. <doc>force install even if there were errors</doc>
  26408. </ignore-errors>
  26409. <loose>
  26410. <shortopt></shortopt>
  26411. <doc>do not check for recommended dependency version</doc>
  26412. </loose>
  26413. </options>
  26414. <doc>
  26415. WARNING: This function is deprecated in favor of using the upgrade command with no params
  26416. Upgrades all packages that have a newer release available. Upgrades are
  26417. done only if there is a release available of the state specified in
  26418. &quot;preferred_state&quot; (currently {config preferred_state}), or a state considered
  26419. more stable.
  26420. </doc>
  26421. </upgrade-all>
  26422. <uninstall>
  26423. <summary>Un-install Package</summary>
  26424. <function>doUninstall</function>
  26425. <shortcut>un</shortcut>
  26426. <options>
  26427. <nodeps>
  26428. <shortopt>n</shortopt>
  26429. <doc>ignore dependencies, uninstall anyway</doc>
  26430. </nodeps>
  26431. <register-only>
  26432. <shortopt>r</shortopt>
  26433. <doc>do not remove files, only register the packages as not installed</doc>
  26434. </register-only>
  26435. <installroot>
  26436. <shortopt>R</shortopt>
  26437. <doc>root directory used when installing files (ala PHP&#039;s INSTALL_ROOT)</doc>
  26438. <arg>DIR</arg>
  26439. </installroot>
  26440. <ignore-errors>
  26441. <shortopt></shortopt>
  26442. <doc>force install even if there were errors</doc>
  26443. </ignore-errors>
  26444. <offline>
  26445. <shortopt>O</shortopt>
  26446. <doc>do not attempt to uninstall remotely</doc>
  26447. </offline>
  26448. </options>
  26449. <doc>[channel/]&lt;package&gt; ...
  26450. Uninstalls one or more PEAR packages. More than one package may be
  26451. specified at once. Prefix with channel name to uninstall from a
  26452. channel not in your default channel ({config default_channel})
  26453. </doc>
  26454. </uninstall>
  26455. <bundle>
  26456. <summary>Unpacks a Pecl Package</summary>
  26457. <function>doBundle</function>
  26458. <shortcut>bun</shortcut>
  26459. <options>
  26460. <destination>
  26461. <shortopt>d</shortopt>
  26462. <doc>Optional destination directory for unpacking (defaults to current path or &quot;ext&quot; if exists)</doc>
  26463. <arg>DIR</arg>
  26464. </destination>
  26465. <force>
  26466. <shortopt>f</shortopt>
  26467. <doc>Force the unpacking even if there were errors in the package</doc>
  26468. </force>
  26469. </options>
  26470. <doc>&lt;package&gt;
  26471. Unpacks a Pecl Package into the selected location. It will download the
  26472. package if needed.
  26473. </doc>
  26474. </bundle>
  26475. <run-scripts>
  26476. <summary>Run Post-Install Scripts bundled with a package</summary>
  26477. <function>doRunScripts</function>
  26478. <shortcut>rs</shortcut>
  26479. <options />
  26480. <doc>&lt;package&gt;
  26481. Run post-installation scripts in package &lt;package&gt;, if any exist.
  26482. </doc>
  26483. </run-scripts>
  26484. </commands>�������������PEAR-1.10.16/PEAR/Command/Install.php���������������������������������������������������������������0000664�0001750�0001750�00000143473�14720722517�016530� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  26485. /**
  26486. * PEAR_Command_Install (install, upgrade, upgrade-all, uninstall, bundle, run-scripts commands)
  26487. *
  26488. * PHP versions 4 and 5
  26489. *
  26490. * @category pear
  26491. * @package PEAR
  26492. * @author Stig Bakken <ssb@php.net>
  26493. * @author Greg Beaver <cellog@php.net>
  26494. * @copyright 1997-2009 The Authors
  26495. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  26496. * @link http://pear.php.net/package/PEAR
  26497. * @since File available since Release 0.1
  26498. */
  26499. /**
  26500. * base class
  26501. */
  26502. require_once 'PEAR/Command/Common.php';
  26503. /**
  26504. * PEAR commands for installation or deinstallation/upgrading of
  26505. * packages.
  26506. *
  26507. * @category pear
  26508. * @package PEAR
  26509. * @author Stig Bakken <ssb@php.net>
  26510. * @author Greg Beaver <cellog@php.net>
  26511. * @copyright 1997-2009 The Authors
  26512. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  26513. * @version Release: 1.10.16
  26514. * @link http://pear.php.net/package/PEAR
  26515. * @since Class available since Release 0.1
  26516. */
  26517. class PEAR_Command_Install extends PEAR_Command_Common
  26518. {
  26519. // {{{ properties
  26520. var $commands = array(
  26521. 'install' => array(
  26522. 'summary' => 'Install Package',
  26523. 'function' => 'doInstall',
  26524. 'shortcut' => 'i',
  26525. 'options' => array(
  26526. 'force' => array(
  26527. 'shortopt' => 'f',
  26528. 'doc' => 'will overwrite newer installed packages',
  26529. ),
  26530. 'loose' => array(
  26531. 'shortopt' => 'l',
  26532. 'doc' => 'do not check for recommended dependency version',
  26533. ),
  26534. 'nodeps' => array(
  26535. 'shortopt' => 'n',
  26536. 'doc' => 'ignore dependencies, install anyway',
  26537. ),
  26538. 'register-only' => array(
  26539. 'shortopt' => 'r',
  26540. 'doc' => 'do not install files, only register the package as installed',
  26541. ),
  26542. 'soft' => array(
  26543. 'shortopt' => 's',
  26544. 'doc' => 'soft install, fail silently, or upgrade if already installed',
  26545. ),
  26546. 'nobuild' => array(
  26547. 'shortopt' => 'B',
  26548. 'doc' => 'don\'t build C extensions',
  26549. ),
  26550. 'configureoptions' => array(
  26551. 'shortopt' => 'D',
  26552. 'arg' => 'OPTION1=VALUE[ OPTION2=VALUE]',
  26553. 'doc' => 'space-delimited list of configure options',
  26554. ),
  26555. 'nocompress' => array(
  26556. 'shortopt' => 'Z',
  26557. 'doc' => 'request uncompressed files when downloading',
  26558. ),
  26559. 'installroot' => array(
  26560. 'shortopt' => 'R',
  26561. 'arg' => 'DIR',
  26562. 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM',
  26563. ),
  26564. 'packagingroot' => array(
  26565. 'shortopt' => 'P',
  26566. 'arg' => 'DIR',
  26567. 'doc' => 'root directory used when packaging files, like RPM packaging',
  26568. ),
  26569. 'ignore-errors' => array(
  26570. 'doc' => 'force install even if there were errors',
  26571. ),
  26572. 'alldeps' => array(
  26573. 'shortopt' => 'a',
  26574. 'doc' => 'install all required and optional dependencies',
  26575. ),
  26576. 'onlyreqdeps' => array(
  26577. 'shortopt' => 'o',
  26578. 'doc' => 'install all required dependencies',
  26579. ),
  26580. 'offline' => array(
  26581. 'shortopt' => 'O',
  26582. 'doc' => 'do not attempt to download any urls or contact channels',
  26583. ),
  26584. 'pretend' => array(
  26585. 'shortopt' => 'p',
  26586. 'doc' => 'Only list the packages that would be downloaded',
  26587. ),
  26588. ),
  26589. 'doc' => '[channel/]<package> ...
  26590. Installs one or more PEAR packages. You can specify a package to
  26591. install in four ways:
  26592. "Package-1.0.tgz" : installs from a local file
  26593. "http://example.com/Package-1.0.tgz" : installs from
  26594. anywhere on the net.
  26595. "package.xml" : installs the package described in
  26596. package.xml. Useful for testing, or for wrapping a PEAR package in
  26597. another package manager such as RPM.
  26598. "Package[-version/state][.tar]" : queries your default channel\'s server
  26599. ({config master_server}) and downloads the newest package with
  26600. the preferred quality/state ({config preferred_state}).
  26601. To retrieve Package version 1.1, use "Package-1.1," to retrieve
  26602. Package state beta, use "Package-beta." To retrieve an uncompressed
  26603. file, append .tar (make sure there is no file by the same name first)
  26604. To download a package from another channel, prefix with the channel name like
  26605. "channel/Package"
  26606. More than one package may be specified at once. It is ok to mix these
  26607. four ways of specifying packages.
  26608. '),
  26609. 'upgrade' => array(
  26610. 'summary' => 'Upgrade Package',
  26611. 'function' => 'doInstall',
  26612. 'shortcut' => 'up',
  26613. 'options' => array(
  26614. 'channel' => array(
  26615. 'shortopt' => 'c',
  26616. 'doc' => 'upgrade packages from a specific channel',
  26617. 'arg' => 'CHAN',
  26618. ),
  26619. 'force' => array(
  26620. 'shortopt' => 'f',
  26621. 'doc' => 'overwrite newer installed packages',
  26622. ),
  26623. 'loose' => array(
  26624. 'shortopt' => 'l',
  26625. 'doc' => 'do not check for recommended dependency version',
  26626. ),
  26627. 'nodeps' => array(
  26628. 'shortopt' => 'n',
  26629. 'doc' => 'ignore dependencies, upgrade anyway',
  26630. ),
  26631. 'register-only' => array(
  26632. 'shortopt' => 'r',
  26633. 'doc' => 'do not install files, only register the package as upgraded',
  26634. ),
  26635. 'nobuild' => array(
  26636. 'shortopt' => 'B',
  26637. 'doc' => 'don\'t build C extensions',
  26638. ),
  26639. 'nocompress' => array(
  26640. 'shortopt' => 'Z',
  26641. 'doc' => 'request uncompressed files when downloading',
  26642. ),
  26643. 'installroot' => array(
  26644. 'shortopt' => 'R',
  26645. 'arg' => 'DIR',
  26646. 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
  26647. ),
  26648. 'ignore-errors' => array(
  26649. 'doc' => 'force install even if there were errors',
  26650. ),
  26651. 'alldeps' => array(
  26652. 'shortopt' => 'a',
  26653. 'doc' => 'install all required and optional dependencies',
  26654. ),
  26655. 'onlyreqdeps' => array(
  26656. 'shortopt' => 'o',
  26657. 'doc' => 'install all required dependencies',
  26658. ),
  26659. 'offline' => array(
  26660. 'shortopt' => 'O',
  26661. 'doc' => 'do not attempt to download any urls or contact channels',
  26662. ),
  26663. 'pretend' => array(
  26664. 'shortopt' => 'p',
  26665. 'doc' => 'Only list the packages that would be downloaded',
  26666. ),
  26667. ),
  26668. 'doc' => '<package> ...
  26669. Upgrades one or more PEAR packages. See documentation for the
  26670. "install" command for ways to specify a package.
  26671. When upgrading, your package will be updated if the provided new
  26672. package has a higher version number (use the -f option if you need to
  26673. upgrade anyway).
  26674. More than one package may be specified at once.
  26675. '),
  26676. 'upgrade-all' => array(
  26677. 'summary' => 'Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters]',
  26678. 'function' => 'doUpgradeAll',
  26679. 'shortcut' => 'ua',
  26680. 'options' => array(
  26681. 'channel' => array(
  26682. 'shortopt' => 'c',
  26683. 'doc' => 'upgrade packages from a specific channel',
  26684. 'arg' => 'CHAN',
  26685. ),
  26686. 'nodeps' => array(
  26687. 'shortopt' => 'n',
  26688. 'doc' => 'ignore dependencies, upgrade anyway',
  26689. ),
  26690. 'register-only' => array(
  26691. 'shortopt' => 'r',
  26692. 'doc' => 'do not install files, only register the package as upgraded',
  26693. ),
  26694. 'nobuild' => array(
  26695. 'shortopt' => 'B',
  26696. 'doc' => 'don\'t build C extensions',
  26697. ),
  26698. 'nocompress' => array(
  26699. 'shortopt' => 'Z',
  26700. 'doc' => 'request uncompressed files when downloading',
  26701. ),
  26702. 'installroot' => array(
  26703. 'shortopt' => 'R',
  26704. 'arg' => 'DIR',
  26705. 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM',
  26706. ),
  26707. 'ignore-errors' => array(
  26708. 'doc' => 'force install even if there were errors',
  26709. ),
  26710. 'loose' => array(
  26711. 'doc' => 'do not check for recommended dependency version',
  26712. ),
  26713. ),
  26714. 'doc' => '
  26715. WARNING: This function is deprecated in favor of using the upgrade command with no params
  26716. Upgrades all packages that have a newer release available. Upgrades are
  26717. done only if there is a release available of the state specified in
  26718. "preferred_state" (currently {config preferred_state}), or a state considered
  26719. more stable.
  26720. '),
  26721. 'uninstall' => array(
  26722. 'summary' => 'Un-install Package',
  26723. 'function' => 'doUninstall',
  26724. 'shortcut' => 'un',
  26725. 'options' => array(
  26726. 'nodeps' => array(
  26727. 'shortopt' => 'n',
  26728. 'doc' => 'ignore dependencies, uninstall anyway',
  26729. ),
  26730. 'register-only' => array(
  26731. 'shortopt' => 'r',
  26732. 'doc' => 'do not remove files, only register the packages as not installed',
  26733. ),
  26734. 'installroot' => array(
  26735. 'shortopt' => 'R',
  26736. 'arg' => 'DIR',
  26737. 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
  26738. ),
  26739. 'ignore-errors' => array(
  26740. 'doc' => 'force install even if there were errors',
  26741. ),
  26742. 'offline' => array(
  26743. 'shortopt' => 'O',
  26744. 'doc' => 'do not attempt to uninstall remotely',
  26745. ),
  26746. ),
  26747. 'doc' => '[channel/]<package> ...
  26748. Uninstalls one or more PEAR packages. More than one package may be
  26749. specified at once. Prefix with channel name to uninstall from a
  26750. channel not in your default channel ({config default_channel})
  26751. '),
  26752. 'bundle' => array(
  26753. 'summary' => 'Unpacks a Pecl Package',
  26754. 'function' => 'doBundle',
  26755. 'shortcut' => 'bun',
  26756. 'options' => array(
  26757. 'destination' => array(
  26758. 'shortopt' => 'd',
  26759. 'arg' => 'DIR',
  26760. 'doc' => 'Optional destination directory for unpacking (defaults to current path or "ext" if exists)',
  26761. ),
  26762. 'force' => array(
  26763. 'shortopt' => 'f',
  26764. 'doc' => 'Force the unpacking even if there were errors in the package',
  26765. ),
  26766. ),
  26767. 'doc' => '<package>
  26768. Unpacks a Pecl Package into the selected location. It will download the
  26769. package if needed.
  26770. '),
  26771. 'run-scripts' => array(
  26772. 'summary' => 'Run Post-Install Scripts bundled with a package',
  26773. 'function' => 'doRunScripts',
  26774. 'shortcut' => 'rs',
  26775. 'options' => array(
  26776. ),
  26777. 'doc' => '<package>
  26778. Run post-installation scripts in package <package>, if any exist.
  26779. '),
  26780. );
  26781. // }}}
  26782. // {{{ constructor
  26783. /**
  26784. * PEAR_Command_Install constructor.
  26785. *
  26786. * @access public
  26787. */
  26788. function __construct(&$ui, &$config)
  26789. {
  26790. parent::__construct($ui, $config);
  26791. }
  26792. // }}}
  26793. /**
  26794. * For unit testing purposes
  26795. */
  26796. function &getDownloader(&$ui, $options, &$config)
  26797. {
  26798. if (!class_exists('PEAR_Downloader')) {
  26799. require_once 'PEAR/Downloader.php';
  26800. }
  26801. $a = new PEAR_Downloader($ui, $options, $config);
  26802. return $a;
  26803. }
  26804. /**
  26805. * For unit testing purposes
  26806. */
  26807. function &getInstaller(&$ui)
  26808. {
  26809. if (!class_exists('PEAR_Installer')) {
  26810. require_once 'PEAR/Installer.php';
  26811. }
  26812. $a = new PEAR_Installer($ui);
  26813. return $a;
  26814. }
  26815. function enableExtension($binaries, $type)
  26816. {
  26817. if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) {
  26818. return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location');
  26819. }
  26820. $ini = $this->_parseIni($phpini);
  26821. if (PEAR::isError($ini)) {
  26822. return $ini;
  26823. }
  26824. $line = 0;
  26825. if ($type == 'extsrc' || $type == 'extbin') {
  26826. $search = 'extensions';
  26827. $enable = 'extension';
  26828. } else {
  26829. $search = 'zend_extensions';
  26830. ob_start();
  26831. phpinfo(INFO_GENERAL);
  26832. $info = ob_get_contents();
  26833. ob_end_clean();
  26834. $debug = function_exists('leak') ? '_debug' : '';
  26835. $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
  26836. $enable = 'zend_extension' . $debug . $ts;
  26837. }
  26838. foreach ($ini[$search] as $line => $extension) {
  26839. if (in_array($extension, $binaries, true) || in_array(
  26840. $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) {
  26841. // already enabled - assume if one is, all are
  26842. return true;
  26843. }
  26844. }
  26845. if ($line) {
  26846. $newini = array_slice($ini['all'], 0, $line);
  26847. } else {
  26848. $newini = array();
  26849. }
  26850. foreach ($binaries as $binary) {
  26851. if ($ini['extension_dir']) {
  26852. $binary = basename($binary);
  26853. }
  26854. $newini[] = $enable . '="' . $binary . '"' . (OS_UNIX ? "\n" : "\r\n");
  26855. }
  26856. $newini = array_merge($newini, array_slice($ini['all'], $line));
  26857. $fp = @fopen($phpini, 'wb');
  26858. if (!$fp) {
  26859. return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing');
  26860. }
  26861. foreach ($newini as $line) {
  26862. fwrite($fp, $line);
  26863. }
  26864. fclose($fp);
  26865. return true;
  26866. }
  26867. function disableExtension($binaries, $type)
  26868. {
  26869. if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) {
  26870. return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location');
  26871. }
  26872. $ini = $this->_parseIni($phpini);
  26873. if (PEAR::isError($ini)) {
  26874. return $ini;
  26875. }
  26876. $line = 0;
  26877. if ($type == 'extsrc' || $type == 'extbin') {
  26878. $search = 'extensions';
  26879. $enable = 'extension';
  26880. } else {
  26881. $search = 'zend_extensions';
  26882. ob_start();
  26883. phpinfo(INFO_GENERAL);
  26884. $info = ob_get_contents();
  26885. ob_end_clean();
  26886. $debug = function_exists('leak') ? '_debug' : '';
  26887. $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
  26888. $enable = 'zend_extension' . $debug . $ts;
  26889. }
  26890. $found = false;
  26891. foreach ($ini[$search] as $line => $extension) {
  26892. if (in_array($extension, $binaries, true) || in_array(
  26893. $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) {
  26894. $found = true;
  26895. break;
  26896. }
  26897. }
  26898. if (!$found) {
  26899. // not enabled
  26900. return true;
  26901. }
  26902. $fp = @fopen($phpini, 'wb');
  26903. if (!$fp) {
  26904. return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing');
  26905. }
  26906. if ($line) {
  26907. $newini = array_slice($ini['all'], 0, $line);
  26908. // delete the enable line
  26909. $newini = array_merge($newini, array_slice($ini['all'], $line + 1));
  26910. } else {
  26911. $newini = array_slice($ini['all'], 1);
  26912. }
  26913. foreach ($newini as $line) {
  26914. fwrite($fp, $line);
  26915. }
  26916. fclose($fp);
  26917. return true;
  26918. }
  26919. function _parseIni($filename)
  26920. {
  26921. if (!file_exists($filename)) {
  26922. return PEAR::raiseError('php.ini "' . $filename . '" does not exist');
  26923. }
  26924. if (filesize($filename) > 300000) {
  26925. return PEAR::raiseError('php.ini "' . $filename . '" is too large, aborting');
  26926. }
  26927. ob_start();
  26928. phpinfo(INFO_GENERAL);
  26929. $info = ob_get_contents();
  26930. ob_end_clean();
  26931. $debug = function_exists('leak') ? '_debug' : '';
  26932. $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
  26933. $zend_extension_line = 'zend_extension' . $debug . $ts;
  26934. $all = @file($filename);
  26935. if ($all === false) {
  26936. return PEAR::raiseError('php.ini "' . $filename .'" could not be read');
  26937. }
  26938. $zend_extensions = $extensions = array();
  26939. // assume this is right, but pull from the php.ini if it is found
  26940. $extension_dir = ini_get('extension_dir');
  26941. foreach ($all as $linenum => $line) {
  26942. $line = trim($line);
  26943. if (!$line) {
  26944. continue;
  26945. }
  26946. if ($line[0] == ';') {
  26947. continue;
  26948. }
  26949. if (strtolower(substr($line, 0, 13)) == 'extension_dir') {
  26950. $line = trim(substr($line, 13));
  26951. if ($line[0] == '=') {
  26952. $x = trim(substr($line, 1));
  26953. $x = explode(';', $x);
  26954. $extension_dir = str_replace('"', '', array_shift($x));
  26955. continue;
  26956. }
  26957. }
  26958. if (strtolower(substr($line, 0, 9)) == 'extension') {
  26959. $line = trim(substr($line, 9));
  26960. if ($line[0] == '=') {
  26961. $x = trim(substr($line, 1));
  26962. $x = explode(';', $x);
  26963. $extensions[$linenum] = str_replace('"', '', array_shift($x));
  26964. continue;
  26965. }
  26966. }
  26967. if (strtolower(substr($line, 0, strlen($zend_extension_line))) ==
  26968. $zend_extension_line) {
  26969. $line = trim(substr($line, strlen($zend_extension_line)));
  26970. if ($line[0] == '=') {
  26971. $x = trim(substr($line, 1));
  26972. $x = explode(';', $x);
  26973. $zend_extensions[$linenum] = str_replace('"', '', array_shift($x));
  26974. continue;
  26975. }
  26976. }
  26977. }
  26978. return array(
  26979. 'extensions' => $extensions,
  26980. 'zend_extensions' => $zend_extensions,
  26981. 'extension_dir' => $extension_dir,
  26982. 'all' => $all,
  26983. );
  26984. }
  26985. // {{{ doInstall()
  26986. function doInstall($command, $options, $params)
  26987. {
  26988. if (!class_exists('PEAR_PackageFile')) {
  26989. require_once 'PEAR/PackageFile.php';
  26990. }
  26991. if (isset($options['installroot']) && isset($options['packagingroot'])) {
  26992. return $this->raiseError('ERROR: cannot use both --installroot and --packagingroot');
  26993. }
  26994. $reg = &$this->config->getRegistry();
  26995. $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel');
  26996. if (!$reg->channelExists($channel)) {
  26997. return $this->raiseError('Channel "' . $channel . '" does not exist');
  26998. }
  26999. if (empty($this->installer)) {
  27000. $this->installer = &$this->getInstaller($this->ui);
  27001. }
  27002. if ($command == 'upgrade' || $command == 'upgrade-all') {
  27003. // If people run the upgrade command but pass nothing, emulate a upgrade-all
  27004. if ($command == 'upgrade' && empty($params)) {
  27005. return $this->doUpgradeAll($command, $options, $params);
  27006. }
  27007. $options['upgrade'] = true;
  27008. } else {
  27009. $packages = $params;
  27010. }
  27011. $instreg = &$reg; // instreg used to check if package is installed
  27012. if (isset($options['packagingroot']) && !isset($options['upgrade'])) {
  27013. $packrootphp_dir = $this->installer->_prependPath(
  27014. $this->config->get('php_dir', null, 'pear.php.net'),
  27015. $options['packagingroot']);
  27016. $metadata_dir = $this->config->get('metadata_dir', null, 'pear.php.net');
  27017. if ($metadata_dir) {
  27018. $metadata_dir = $this->installer->_prependPath(
  27019. $metadata_dir,
  27020. $options['packagingroot']);
  27021. }
  27022. $instreg = new PEAR_Registry($packrootphp_dir, false, false, $metadata_dir); // other instreg!
  27023. if ($this->config->get('verbose') > 2) {
  27024. $this->ui->outputData('using package root: ' . $options['packagingroot']);
  27025. }
  27026. }
  27027. $abstractpackages = $otherpackages = array();
  27028. // parse params
  27029. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  27030. foreach ($params as $param) {
  27031. if (strpos($param, 'http://') === 0) {
  27032. $otherpackages[] = $param;
  27033. continue;
  27034. }
  27035. if (strpos($param, 'channel://') === false && @file_exists($param)) {
  27036. if (isset($options['force'])) {
  27037. $otherpackages[] = $param;
  27038. continue;
  27039. }
  27040. $pkg = new PEAR_PackageFile($this->config);
  27041. $pf = $pkg->fromAnyFile($param, PEAR_VALIDATE_DOWNLOADING);
  27042. if (PEAR::isError($pf)) {
  27043. $otherpackages[] = $param;
  27044. continue;
  27045. }
  27046. $exists = $reg->packageExists($pf->getPackage(), $pf->getChannel());
  27047. $pversion = $reg->packageInfo($pf->getPackage(), 'version', $pf->getChannel());
  27048. $version_compare = version_compare($pf->getVersion(), $pversion, '<=');
  27049. if ($exists && $version_compare) {
  27050. if ($this->config->get('verbose')) {
  27051. $this->ui->outputData('Ignoring installed package ' .
  27052. $reg->parsedPackageNameToString(
  27053. array('package' => $pf->getPackage(),
  27054. 'channel' => $pf->getChannel()), true));
  27055. }
  27056. continue;
  27057. }
  27058. $otherpackages[] = $param;
  27059. continue;
  27060. }
  27061. $e = $reg->parsePackageName($param, $channel);
  27062. if (PEAR::isError($e)) {
  27063. $otherpackages[] = $param;
  27064. } else {
  27065. $abstractpackages[] = $e;
  27066. }
  27067. }
  27068. PEAR::staticPopErrorHandling();
  27069. // if there are any local package .tgz or remote static url, we can't
  27070. // filter. The filter only works for abstract packages
  27071. if (count($abstractpackages) && !isset($options['force'])) {
  27072. // when not being forced, only do necessary upgrades/installs
  27073. if (isset($options['upgrade'])) {
  27074. $abstractpackages = $this->_filterUptodatePackages($abstractpackages, $command);
  27075. } else {
  27076. $count = count($abstractpackages);
  27077. foreach ($abstractpackages as $i => $package) {
  27078. if (isset($package['group'])) {
  27079. // do not filter out install groups
  27080. continue;
  27081. }
  27082. if ($instreg->packageExists($package['package'], $package['channel'])) {
  27083. if ($count > 1) {
  27084. if ($this->config->get('verbose')) {
  27085. $this->ui->outputData('Ignoring installed package ' .
  27086. $reg->parsedPackageNameToString($package, true));
  27087. }
  27088. unset($abstractpackages[$i]);
  27089. } elseif ($count === 1) {
  27090. // Lets try to upgrade it since it's already installed
  27091. $options['upgrade'] = true;
  27092. }
  27093. }
  27094. }
  27095. }
  27096. $abstractpackages =
  27097. array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
  27098. } elseif (count($abstractpackages)) {
  27099. $abstractpackages =
  27100. array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
  27101. }
  27102. $packages = array_merge($abstractpackages, $otherpackages);
  27103. if (!count($packages)) {
  27104. $c = '';
  27105. if (isset($options['channel'])){
  27106. $c .= ' in channel "' . $options['channel'] . '"';
  27107. }
  27108. $this->ui->outputData('Nothing to ' . $command . $c);
  27109. return true;
  27110. }
  27111. $this->downloader = &$this->getDownloader($this->ui, $options, $this->config);
  27112. $errors = $downloaded = $binaries = array();
  27113. $downloaded = &$this->downloader->download($packages);
  27114. if (PEAR::isError($downloaded)) {
  27115. return $this->raiseError($downloaded);
  27116. }
  27117. $errors = $this->downloader->getErrorMsgs();
  27118. if (count($errors)) {
  27119. $err = array();
  27120. $err['data'] = array();
  27121. foreach ($errors as $error) {
  27122. if ($error !== null) {
  27123. $err['data'][] = array($error);
  27124. }
  27125. }
  27126. if (!empty($err['data'])) {
  27127. $err['headline'] = 'Install Errors';
  27128. $this->ui->outputData($err);
  27129. }
  27130. if (!count($downloaded)) {
  27131. return $this->raiseError("$command failed");
  27132. }
  27133. }
  27134. $data = array(
  27135. 'headline' => 'Packages that would be Installed'
  27136. );
  27137. if (isset($options['pretend'])) {
  27138. foreach ($downloaded as $package) {
  27139. $data['data'][] = array($reg->parsedPackageNameToString($package->getParsedPackage()));
  27140. }
  27141. $this->ui->outputData($data, 'pretend');
  27142. return true;
  27143. }
  27144. $this->installer->setOptions($options);
  27145. $this->installer->sortPackagesForInstall($downloaded);
  27146. if (PEAR::isError($err = $this->installer->setDownloadedPackages($downloaded))) {
  27147. $this->raiseError($err->getMessage());
  27148. return true;
  27149. }
  27150. $binaries = $extrainfo = array();
  27151. foreach ($downloaded as $param) {
  27152. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  27153. $info = $this->installer->install($param, $options);
  27154. PEAR::staticPopErrorHandling();
  27155. if (PEAR::isError($info)) {
  27156. $oldinfo = $info;
  27157. $pkg = &$param->getPackageFile();
  27158. if ($info->getCode() != PEAR_INSTALLER_NOBINARY) {
  27159. if (!($info = $pkg->installBinary($this->installer))) {
  27160. return $this->raiseError('ERROR: ' .$oldinfo->getMessage());
  27161. }
  27162. // we just installed a different package than requested,
  27163. // let's change the param and info so that the rest of this works
  27164. $param = $info[0];
  27165. $info = $info[1];
  27166. }
  27167. }
  27168. if (!is_array($info)) {
  27169. return $this->raiseError("$command failed");
  27170. }
  27171. if ($param->getPackageType() == 'extsrc' ||
  27172. $param->getPackageType() == 'extbin' ||
  27173. $param->getPackageType() == 'zendextsrc' ||
  27174. $param->getPackageType() == 'zendextbin'
  27175. ) {
  27176. $pkg = &$param->getPackageFile();
  27177. if ($instbin = $pkg->getInstalledBinary()) {
  27178. $instpkg = &$instreg->getPackage($instbin, $pkg->getChannel());
  27179. } else {
  27180. $instpkg = &$instreg->getPackage($pkg->getPackage(), $pkg->getChannel());
  27181. }
  27182. foreach ($instpkg->getFilelist() as $name => $atts) {
  27183. $pinfo = pathinfo($atts['installed_as']);
  27184. if (!isset($pinfo['extension']) ||
  27185. in_array($pinfo['extension'], array('c', 'h'))
  27186. ) {
  27187. continue; // make sure we don't match php_blah.h
  27188. }
  27189. if ((strpos($pinfo['basename'], 'php_') === 0 &&
  27190. $pinfo['extension'] == 'dll') ||
  27191. // most unices
  27192. $pinfo['extension'] == 'so' ||
  27193. // hp-ux
  27194. $pinfo['extension'] == 'sl') {
  27195. $binaries[] = array($atts['installed_as'], $pinfo);
  27196. break;
  27197. }
  27198. }
  27199. if (count($binaries)) {
  27200. foreach ($binaries as $pinfo) {
  27201. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  27202. $ret = $this->enableExtension(array($pinfo[0]), $param->getPackageType());
  27203. PEAR::staticPopErrorHandling();
  27204. if (PEAR::isError($ret)) {
  27205. $extrainfo[] = $ret->getMessage();
  27206. if ($param->getPackageType() == 'extsrc' ||
  27207. $param->getPackageType() == 'extbin') {
  27208. $exttype = 'extension';
  27209. $extpath = $pinfo[1]['basename'];
  27210. } else {
  27211. $exttype = 'zend_extension';
  27212. $extpath = $atts['installed_as'];
  27213. }
  27214. $extrainfo[] = 'You should add "' . $exttype . '=' .
  27215. $extpath . '" to php.ini';
  27216. } else {
  27217. $extrainfo[] = 'Extension ' . $instpkg->getProvidesExtension() .
  27218. ' enabled in php.ini';
  27219. }
  27220. }
  27221. }
  27222. }
  27223. if ($this->config->get('verbose') > 0) {
  27224. $chan = $param->getChannel();
  27225. $label = $reg->parsedPackageNameToString(
  27226. array(
  27227. 'channel' => $chan,
  27228. 'package' => $param->getPackage(),
  27229. 'version' => $param->getVersion(),
  27230. ));
  27231. $out = array('data' => "$command ok: $label");
  27232. if (isset($info['release_warnings'])) {
  27233. $out['release_warnings'] = $info['release_warnings'];
  27234. }
  27235. $this->ui->outputData($out, $command);
  27236. if (!isset($options['register-only']) && !isset($options['offline'])) {
  27237. if ($this->config->isDefinedLayer('ftp')) {
  27238. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  27239. $info = $this->installer->ftpInstall($param);
  27240. PEAR::staticPopErrorHandling();
  27241. if (PEAR::isError($info)) {
  27242. $this->ui->outputData($info->getMessage());
  27243. $this->ui->outputData("remote install failed: $label");
  27244. } else {
  27245. $this->ui->outputData("remote install ok: $label");
  27246. }
  27247. }
  27248. }
  27249. }
  27250. $deps = $param->getDeps();
  27251. if ($deps) {
  27252. if (isset($deps['group'])) {
  27253. $groups = $deps['group'];
  27254. if (!isset($groups[0])) {
  27255. $groups = array($groups);
  27256. }
  27257. foreach ($groups as $group) {
  27258. if ($group['attribs']['name'] == 'default') {
  27259. // default group is always installed, unless the user
  27260. // explicitly chooses to install another group
  27261. continue;
  27262. }
  27263. $extrainfo[] = $param->getPackage() . ': Optional feature ' .
  27264. $group['attribs']['name'] . ' available (' .
  27265. $group['attribs']['hint'] . ')';
  27266. }
  27267. $extrainfo[] = $param->getPackage() .
  27268. ': To install optional features use "pear install ' .
  27269. $reg->parsedPackageNameToString(
  27270. array('package' => $param->getPackage(),
  27271. 'channel' => $param->getChannel()), true) .
  27272. '#featurename"';
  27273. }
  27274. }
  27275. $pkg = &$instreg->getPackage($param->getPackage(), $param->getChannel());
  27276. // $pkg may be NULL if install is a 'fake' install via --packagingroot
  27277. if (is_object($pkg)) {
  27278. $pkg->setConfig($this->config);
  27279. if ($list = $pkg->listPostinstallScripts()) {
  27280. $pn = $reg->parsedPackageNameToString(array('channel' =>
  27281. $param->getChannel(), 'package' => $param->getPackage()), true);
  27282. $extrainfo[] = $pn . ' has post-install scripts:';
  27283. foreach ($list as $file) {
  27284. $extrainfo[] = $file;
  27285. }
  27286. $extrainfo[] = $param->getPackage() .
  27287. ': Use "pear run-scripts ' . $pn . '" to finish setup.';
  27288. $extrainfo[] = 'DO NOT RUN SCRIPTS FROM UNTRUSTED SOURCES';
  27289. }
  27290. }
  27291. }
  27292. if (count($extrainfo)) {
  27293. foreach ($extrainfo as $info) {
  27294. $this->ui->outputData($info);
  27295. }
  27296. }
  27297. return true;
  27298. }
  27299. // }}}
  27300. // {{{ doUpgradeAll()
  27301. function doUpgradeAll($command, $options, $params)
  27302. {
  27303. $reg = &$this->config->getRegistry();
  27304. $upgrade = array();
  27305. if (isset($options['channel'])) {
  27306. $channels = array($options['channel']);
  27307. } else {
  27308. $channels = $reg->listChannels();
  27309. }
  27310. foreach ($channels as $channel) {
  27311. if ($channel == '__uri') {
  27312. continue;
  27313. }
  27314. // parse name with channel
  27315. foreach ($reg->listPackages($channel) as $name) {
  27316. $upgrade[] = $reg->parsedPackageNameToString(array(
  27317. 'channel' => $channel,
  27318. 'package' => $name
  27319. ));
  27320. }
  27321. }
  27322. $err = $this->doInstall($command, $options, $upgrade);
  27323. if (PEAR::isError($err)) {
  27324. $this->ui->outputData($err->getMessage(), $command);
  27325. }
  27326. }
  27327. // }}}
  27328. // {{{ doUninstall()
  27329. function doUninstall($command, $options, $params)
  27330. {
  27331. if (count($params) < 1) {
  27332. return $this->raiseError("Please supply the package(s) you want to uninstall");
  27333. }
  27334. if (empty($this->installer)) {
  27335. $this->installer = &$this->getInstaller($this->ui);
  27336. }
  27337. if (isset($options['remoteconfig'])) {
  27338. $e = $this->config->readFTPConfigFile($options['remoteconfig']);
  27339. if (!PEAR::isError($e)) {
  27340. $this->installer->setConfig($this->config);
  27341. }
  27342. }
  27343. $reg = &$this->config->getRegistry();
  27344. $newparams = array();
  27345. $binaries = array();
  27346. $badparams = array();
  27347. foreach ($params as $pkg) {
  27348. $channel = $this->config->get('default_channel');
  27349. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  27350. $parsed = $reg->parsePackageName($pkg, $channel);
  27351. PEAR::staticPopErrorHandling();
  27352. if (!$parsed || PEAR::isError($parsed)) {
  27353. $badparams[] = $pkg;
  27354. continue;
  27355. }
  27356. $package = $parsed['package'];
  27357. $channel = $parsed['channel'];
  27358. $info = &$reg->getPackage($package, $channel);
  27359. if ($info === null &&
  27360. ($channel == 'pear.php.net' || $channel == 'pecl.php.net')) {
  27361. // make sure this isn't a package that has flipped from pear to pecl but
  27362. // used a package.xml 1.0
  27363. $testc = ($channel == 'pear.php.net') ? 'pecl.php.net' : 'pear.php.net';
  27364. $info = &$reg->getPackage($package, $testc);
  27365. if ($info !== null) {
  27366. $channel = $testc;
  27367. }
  27368. }
  27369. if ($info === null) {
  27370. $badparams[] = $pkg;
  27371. } else {
  27372. $newparams[] = &$info;
  27373. // check for binary packages (this is an alias for those packages if so)
  27374. if ($installedbinary = $info->getInstalledBinary()) {
  27375. $this->ui->log('adding binary package ' .
  27376. $reg->parsedPackageNameToString(array('channel' => $channel,
  27377. 'package' => $installedbinary), true));
  27378. $newparams[] = &$reg->getPackage($installedbinary, $channel);
  27379. }
  27380. // add the contents of a dependency group to the list of installed packages
  27381. if (isset($parsed['group'])) {
  27382. $group = $info->getDependencyGroup($parsed['group']);
  27383. if ($group) {
  27384. $installed = $reg->getInstalledGroup($group);
  27385. if ($installed) {
  27386. foreach ($installed as $i => $p) {
  27387. $newparams[] = &$installed[$i];
  27388. }
  27389. }
  27390. }
  27391. }
  27392. }
  27393. }
  27394. $err = $this->installer->sortPackagesForUninstall($newparams);
  27395. if (PEAR::isError($err)) {
  27396. $this->ui->outputData($err->getMessage(), $command);
  27397. return true;
  27398. }
  27399. $params = $newparams;
  27400. // twist this to use it to check on whether dependent packages are also being uninstalled
  27401. // for circular dependencies like subpackages
  27402. $this->installer->setUninstallPackages($newparams);
  27403. $params = array_merge($params, $badparams);
  27404. $binaries = array();
  27405. foreach ($params as $pkg) {
  27406. $this->installer->pushErrorHandling(PEAR_ERROR_RETURN);
  27407. if ($err = $this->installer->uninstall($pkg, $options)) {
  27408. $this->installer->popErrorHandling();
  27409. if (PEAR::isError($err)) {
  27410. $this->ui->outputData($err->getMessage(), $command);
  27411. continue;
  27412. }
  27413. if ($pkg->getPackageType() == 'extsrc' ||
  27414. $pkg->getPackageType() == 'extbin' ||
  27415. $pkg->getPackageType() == 'zendextsrc' ||
  27416. $pkg->getPackageType() == 'zendextbin') {
  27417. if ($instbin = $pkg->getInstalledBinary()) {
  27418. continue; // this will be uninstalled later
  27419. }
  27420. foreach ($pkg->getFilelist() as $name => $atts) {
  27421. $pinfo = pathinfo($atts['installed_as']);
  27422. if (!isset($pinfo['extension']) ||
  27423. in_array($pinfo['extension'], array('c', 'h'))) {
  27424. continue; // make sure we don't match php_blah.h
  27425. }
  27426. if ((strpos($pinfo['basename'], 'php_') === 0 &&
  27427. $pinfo['extension'] == 'dll') ||
  27428. // most unices
  27429. $pinfo['extension'] == 'so' ||
  27430. // hp-ux
  27431. $pinfo['extension'] == 'sl') {
  27432. $binaries[] = array($atts['installed_as'], $pinfo);
  27433. break;
  27434. }
  27435. }
  27436. if (count($binaries)) {
  27437. foreach ($binaries as $pinfo) {
  27438. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  27439. $ret = $this->disableExtension(array($pinfo[0]), $pkg->getPackageType());
  27440. PEAR::staticPopErrorHandling();
  27441. if (PEAR::isError($ret)) {
  27442. $extrainfo[] = $ret->getMessage();
  27443. if ($pkg->getPackageType() == 'extsrc' ||
  27444. $pkg->getPackageType() == 'extbin') {
  27445. $exttype = 'extension';
  27446. } else {
  27447. ob_start();
  27448. phpinfo(INFO_GENERAL);
  27449. $info = ob_get_contents();
  27450. ob_end_clean();
  27451. $debug = function_exists('leak') ? '_debug' : '';
  27452. $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
  27453. $exttype = 'zend_extension' . $debug . $ts;
  27454. }
  27455. $this->ui->outputData('Unable to remove "' . $exttype . '=' .
  27456. $pinfo[1]['basename'] . '" from php.ini', $command);
  27457. } else {
  27458. $this->ui->outputData('Extension ' . $pkg->getProvidesExtension() .
  27459. ' disabled in php.ini', $command);
  27460. }
  27461. }
  27462. }
  27463. }
  27464. $savepkg = $pkg;
  27465. if ($this->config->get('verbose') > 0) {
  27466. if (is_object($pkg)) {
  27467. $pkg = $reg->parsedPackageNameToString($pkg);
  27468. }
  27469. $this->ui->outputData("uninstall ok: $pkg", $command);
  27470. }
  27471. if (!isset($options['offline']) && is_object($savepkg) &&
  27472. defined('PEAR_REMOTEINSTALL_OK')) {
  27473. if ($this->config->isDefinedLayer('ftp')) {
  27474. $this->installer->pushErrorHandling(PEAR_ERROR_RETURN);
  27475. $info = $this->installer->ftpUninstall($savepkg);
  27476. $this->installer->popErrorHandling();
  27477. if (PEAR::isError($info)) {
  27478. $this->ui->outputData($info->getMessage());
  27479. $this->ui->outputData("remote uninstall failed: $pkg");
  27480. } else {
  27481. $this->ui->outputData("remote uninstall ok: $pkg");
  27482. }
  27483. }
  27484. }
  27485. } else {
  27486. $this->installer->popErrorHandling();
  27487. if (!is_object($pkg)) {
  27488. return $this->raiseError("uninstall failed: $pkg");
  27489. }
  27490. $pkg = $reg->parsedPackageNameToString($pkg);
  27491. }
  27492. }
  27493. return true;
  27494. }
  27495. // }}}
  27496. // }}}
  27497. // {{{ doBundle()
  27498. /*
  27499. (cox) It just downloads and untars the package, does not do
  27500. any check that the PEAR_Installer::_installFile() does.
  27501. */
  27502. function doBundle($command, $options, $params)
  27503. {
  27504. $opts = array(
  27505. 'force' => true,
  27506. 'nodeps' => true,
  27507. 'soft' => true,
  27508. 'downloadonly' => true
  27509. );
  27510. $downloader = &$this->getDownloader($this->ui, $opts, $this->config);
  27511. $reg = &$this->config->getRegistry();
  27512. if (count($params) < 1) {
  27513. return $this->raiseError("Please supply the package you want to bundle");
  27514. }
  27515. if (isset($options['destination'])) {
  27516. if (!is_dir($options['destination'])) {
  27517. System::mkdir('-p ' . $options['destination']);
  27518. }
  27519. $dest = realpath($options['destination']);
  27520. } else {
  27521. $pwd = getcwd();
  27522. $dir = $pwd . DIRECTORY_SEPARATOR . 'ext';
  27523. $dest = is_dir($dir) ? $dir : $pwd;
  27524. }
  27525. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  27526. $err = $downloader->setDownloadDir($dest);
  27527. PEAR::staticPopErrorHandling();
  27528. if (PEAR::isError($err)) {
  27529. return PEAR::raiseError('download directory "' . $dest .
  27530. '" is not writeable.');
  27531. }
  27532. $result = &$downloader->download(array($params[0]));
  27533. if (PEAR::isError($result)) {
  27534. return $result;
  27535. }
  27536. if (!isset($result[0])) {
  27537. return $this->raiseError('unable to unpack ' . $params[0]);
  27538. }
  27539. $pkgfile = &$result[0]->getPackageFile();
  27540. $pkgname = $pkgfile->getName();
  27541. $pkgversion = $pkgfile->getVersion();
  27542. // Unpacking -------------------------------------------------
  27543. $dest .= DIRECTORY_SEPARATOR . $pkgname;
  27544. $orig = $pkgname . '-' . $pkgversion;
  27545. $tar = new Archive_Tar($pkgfile->getArchiveFile());
  27546. if (!$tar->extractModify($dest, $orig)) {
  27547. return $this->raiseError('unable to unpack ' . $pkgfile->getArchiveFile());
  27548. }
  27549. $this->ui->outputData("Package ready at '$dest'");
  27550. // }}}
  27551. }
  27552. // }}}
  27553. function doRunScripts($command, $options, $params)
  27554. {
  27555. if (!isset($params[0])) {
  27556. return $this->raiseError('run-scripts expects 1 parameter: a package name');
  27557. }
  27558. $reg = &$this->config->getRegistry();
  27559. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  27560. $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
  27561. PEAR::staticPopErrorHandling();
  27562. if (PEAR::isError($parsed)) {
  27563. return $this->raiseError($parsed);
  27564. }
  27565. $package = &$reg->getPackage($parsed['package'], $parsed['channel']);
  27566. if (!is_object($package)) {
  27567. return $this->raiseError('Could not retrieve package "' . $params[0] . '" from registry');
  27568. }
  27569. $package->setConfig($this->config);
  27570. $package->runPostinstallScripts();
  27571. $this->ui->outputData('Install scripts complete', $command);
  27572. return true;
  27573. }
  27574. /**
  27575. * Given a list of packages, filter out those ones that are already up to date
  27576. *
  27577. * @param $packages: packages, in parsed array format !
  27578. * @return list of packages that can be upgraded
  27579. */
  27580. function _filterUptodatePackages($packages, $command)
  27581. {
  27582. $reg = &$this->config->getRegistry();
  27583. $latestReleases = array();
  27584. $ret = array();
  27585. foreach ($packages as $package) {
  27586. if (isset($package['group'])) {
  27587. $ret[] = $package;
  27588. continue;
  27589. }
  27590. $channel = $package['channel'];
  27591. $name = $package['package'];
  27592. if (!$reg->packageExists($name, $channel)) {
  27593. $ret[] = $package;
  27594. continue;
  27595. }
  27596. if (!isset($latestReleases[$channel])) {
  27597. // fill in cache for this channel
  27598. $chan = $reg->getChannel($channel);
  27599. if (PEAR::isError($chan)) {
  27600. return $this->raiseError($chan);
  27601. }
  27602. $base2 = false;
  27603. $preferred_mirror = $this->config->get('preferred_mirror', null, $channel);
  27604. if ($chan->supportsREST($preferred_mirror) &&
  27605. (
  27606. //($base2 = $chan->getBaseURL('REST1.4', $preferred_mirror)) ||
  27607. ($base = $chan->getBaseURL('REST1.0', $preferred_mirror))
  27608. )
  27609. ) {
  27610. $dorest = true;
  27611. }
  27612. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  27613. if (!isset($package['state'])) {
  27614. $state = $this->config->get('preferred_state', null, $channel);
  27615. } else {
  27616. $state = $package['state'];
  27617. }
  27618. if ($dorest) {
  27619. if ($base2) {
  27620. $rest = &$this->config->getREST('1.4', array());
  27621. $base = $base2;
  27622. } else {
  27623. $rest = &$this->config->getREST('1.0', array());
  27624. }
  27625. $installed = array_flip($reg->listPackages($channel));
  27626. $latest = $rest->listLatestUpgrades($base, $state, $installed, $channel, $reg);
  27627. }
  27628. PEAR::staticPopErrorHandling();
  27629. if (PEAR::isError($latest)) {
  27630. $this->ui->outputData('Error getting channel info from ' . $channel .
  27631. ': ' . $latest->getMessage());
  27632. continue;
  27633. }
  27634. $latestReleases[$channel] = array_change_key_case($latest);
  27635. }
  27636. // check package for latest release
  27637. $name_lower = strtolower($name);
  27638. if (isset($latestReleases[$channel][$name_lower])) {
  27639. // if not set, up to date
  27640. $inst_version = $reg->packageInfo($name, 'version', $channel);
  27641. $channel_version = $latestReleases[$channel][$name_lower]['version'];
  27642. if (version_compare($channel_version, $inst_version, 'le')) {
  27643. // installed version is up-to-date
  27644. continue;
  27645. }
  27646. // maintain BC
  27647. if ($command == 'upgrade-all') {
  27648. $this->ui->outputData(array('data' => 'Will upgrade ' .
  27649. $reg->parsedPackageNameToString($package)), $command);
  27650. }
  27651. $ret[] = $package;
  27652. }
  27653. }
  27654. return $ret;
  27655. }
  27656. }
  27657. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Mirror.xml����������������������������������������������������������������0000644�0001750�0001750�00000001151�14720722517�016365� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<commands version="1.0">
  27658. <download-all>
  27659. <summary>Downloads each available package from the default channel</summary>
  27660. <function>doDownloadAll</function>
  27661. <shortcut>da</shortcut>
  27662. <options>
  27663. <channel>
  27664. <shortopt>c</shortopt>
  27665. <doc>specify a channel other than the default channel</doc>
  27666. <arg>CHAN</arg>
  27667. </channel>
  27668. </options>
  27669. <doc>
  27670. Requests a list of available packages from the default channel ({config default_channel})
  27671. and downloads them to current working directory. Note: only
  27672. packages within preferred_state ({config preferred_state}) will be downloaded</doc>
  27673. </download-all>
  27674. </commands>�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Mirror.php����������������������������������������������������������������0000644�0001750�0001750�00000010636�14720722517�016364� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  27675. /**
  27676. * PEAR_Command_Mirror (download-all command)
  27677. *
  27678. * PHP versions 4 and 5
  27679. *
  27680. * @category pear
  27681. * @package PEAR
  27682. * @author Alexander Merz <alexmerz@php.net>
  27683. * @copyright 1997-2009 The Authors
  27684. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  27685. * @link http://pear.php.net/package/PEAR
  27686. * @since File available since Release 1.2.0
  27687. */
  27688. /**
  27689. * base class
  27690. */
  27691. require_once 'PEAR/Command/Common.php';
  27692. /**
  27693. * PEAR commands for providing file mirrors
  27694. *
  27695. * @category pear
  27696. * @package PEAR
  27697. * @author Alexander Merz <alexmerz@php.net>
  27698. * @copyright 1997-2009 The Authors
  27699. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  27700. * @version Release: 1.10.16
  27701. * @link http://pear.php.net/package/PEAR
  27702. * @since Class available since Release 1.2.0
  27703. */
  27704. class PEAR_Command_Mirror extends PEAR_Command_Common
  27705. {
  27706. var $commands = array(
  27707. 'download-all' => array(
  27708. 'summary' => 'Downloads each available package from the default channel',
  27709. 'function' => 'doDownloadAll',
  27710. 'shortcut' => 'da',
  27711. 'options' => array(
  27712. 'channel' =>
  27713. array(
  27714. 'shortopt' => 'c',
  27715. 'doc' => 'specify a channel other than the default channel',
  27716. 'arg' => 'CHAN',
  27717. ),
  27718. ),
  27719. 'doc' => '
  27720. Requests a list of available packages from the default channel ({config default_channel})
  27721. and downloads them to current working directory. Note: only
  27722. packages within preferred_state ({config preferred_state}) will be downloaded'
  27723. ),
  27724. );
  27725. /**
  27726. * PEAR_Command_Mirror constructor.
  27727. *
  27728. * @access public
  27729. * @param object PEAR_Frontend a reference to an frontend
  27730. * @param object PEAR_Config a reference to the configuration data
  27731. */
  27732. function __construct(&$ui, &$config)
  27733. {
  27734. parent::__construct($ui, $config);
  27735. }
  27736. /**
  27737. * For unit-testing
  27738. */
  27739. function &factory($a)
  27740. {
  27741. $a = &PEAR_Command::factory($a, $this->config);
  27742. return $a;
  27743. }
  27744. /**
  27745. * retrieves a list of avaible Packages from master server
  27746. * and downloads them
  27747. *
  27748. * @access public
  27749. * @param string $command the command
  27750. * @param array $options the command options before the command
  27751. * @param array $params the stuff after the command name
  27752. * @return bool true if successful
  27753. * @throw PEAR_Error
  27754. */
  27755. function doDownloadAll($command, $options, $params)
  27756. {
  27757. $savechannel = $this->config->get('default_channel');
  27758. $reg = &$this->config->getRegistry();
  27759. $channel = isset($options['channel']) ? $options['channel'] :
  27760. $this->config->get('default_channel');
  27761. if (!$reg->channelExists($channel)) {
  27762. $this->config->set('default_channel', $savechannel);
  27763. return $this->raiseError('Channel "' . $channel . '" does not exist');
  27764. }
  27765. $this->config->set('default_channel', $channel);
  27766. $this->ui->outputData('Using Channel ' . $this->config->get('default_channel'));
  27767. $chan = $reg->getChannel($channel);
  27768. if (PEAR::isError($chan)) {
  27769. return $this->raiseError($chan);
  27770. }
  27771. if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
  27772. $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
  27773. $rest = &$this->config->getREST('1.0', array());
  27774. $remoteInfo = array_flip($rest->listPackages($base, $channel));
  27775. }
  27776. if (PEAR::isError($remoteInfo)) {
  27777. return $remoteInfo;
  27778. }
  27779. $cmd = &$this->factory("download");
  27780. if (PEAR::isError($cmd)) {
  27781. return $cmd;
  27782. }
  27783. $this->ui->outputData('Using Preferred State of ' .
  27784. $this->config->get('preferred_state'));
  27785. $this->ui->outputData('Gathering release information, please wait...');
  27786. /**
  27787. * Error handling not necessary, because already done by
  27788. * the download command
  27789. */
  27790. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  27791. $err = $cmd->run('download', array('downloadonly' => true), array_keys($remoteInfo));
  27792. PEAR::staticPopErrorHandling();
  27793. $this->config->set('default_channel', $savechannel);
  27794. if (PEAR::isError($err)) {
  27795. $this->ui->outputData($err->getMessage());
  27796. }
  27797. return true;
  27798. }
  27799. }
  27800. ��������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Package.xml���������������������������������������������������������������0000644�0001750�0001750�00000016066�14720722517�016461� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<commands version="1.0">
  27801. <package>
  27802. <summary>Build Package</summary>
  27803. <function>doPackage</function>
  27804. <shortcut>p</shortcut>
  27805. <options>
  27806. <nocompress>
  27807. <shortopt>Z</shortopt>
  27808. <doc>Do not gzip the package file</doc>
  27809. </nocompress>
  27810. <showname>
  27811. <shortopt>n</shortopt>
  27812. <doc>Print the name of the packaged file.</doc>
  27813. </showname>
  27814. </options>
  27815. <doc>[descfile] [descfile2]
  27816. Creates a PEAR package from its description file (usually called
  27817. package.xml). If a second packagefile is passed in, then
  27818. the packager will check to make sure that one is a package.xml
  27819. version 1.0, and the other is a package.xml version 2.0. The
  27820. package.xml version 1.0 will be saved as &quot;package.xml&quot; in the archive,
  27821. and the other as &quot;package2.xml&quot; in the archive&quot;
  27822. </doc>
  27823. </package>
  27824. <package-validate>
  27825. <summary>Validate Package Consistency</summary>
  27826. <function>doPackageValidate</function>
  27827. <shortcut>pv</shortcut>
  27828. <options />
  27829. <doc>
  27830. </doc>
  27831. </package-validate>
  27832. <cvsdiff>
  27833. <summary>Run a &quot;cvs diff&quot; for all files in a package</summary>
  27834. <function>doCvsDiff</function>
  27835. <shortcut>cd</shortcut>
  27836. <options>
  27837. <quiet>
  27838. <shortopt>q</shortopt>
  27839. <doc>Be quiet</doc>
  27840. </quiet>
  27841. <reallyquiet>
  27842. <shortopt>Q</shortopt>
  27843. <doc>Be really quiet</doc>
  27844. </reallyquiet>
  27845. <date>
  27846. <shortopt>D</shortopt>
  27847. <doc>Diff against revision of DATE</doc>
  27848. <arg>DATE</arg>
  27849. </date>
  27850. <release>
  27851. <shortopt>R</shortopt>
  27852. <doc>Diff against tag for package release REL</doc>
  27853. <arg>REL</arg>
  27854. </release>
  27855. <revision>
  27856. <shortopt>r</shortopt>
  27857. <doc>Diff against revision REV</doc>
  27858. <arg>REV</arg>
  27859. </revision>
  27860. <context>
  27861. <shortopt>c</shortopt>
  27862. <doc>Generate context diff</doc>
  27863. </context>
  27864. <unified>
  27865. <shortopt>u</shortopt>
  27866. <doc>Generate unified diff</doc>
  27867. </unified>
  27868. <ignore-case>
  27869. <shortopt>i</shortopt>
  27870. <doc>Ignore case, consider upper- and lower-case letters equivalent</doc>
  27871. </ignore-case>
  27872. <ignore-whitespace>
  27873. <shortopt>b</shortopt>
  27874. <doc>Ignore changes in amount of white space</doc>
  27875. </ignore-whitespace>
  27876. <ignore-blank-lines>
  27877. <shortopt>B</shortopt>
  27878. <doc>Ignore changes that insert or delete blank lines</doc>
  27879. </ignore-blank-lines>
  27880. <brief>
  27881. <shortopt></shortopt>
  27882. <doc>Report only whether the files differ, no details</doc>
  27883. </brief>
  27884. <dry-run>
  27885. <shortopt>n</shortopt>
  27886. <doc>Don&#039;t do anything, just pretend</doc>
  27887. </dry-run>
  27888. </options>
  27889. <doc>&lt;package.xml&gt;
  27890. Compares all the files in a package. Without any options, this
  27891. command will compare the current code with the last checked-in code.
  27892. Using the -r or -R option you may compare the current code with that
  27893. of a specific release.
  27894. </doc>
  27895. </cvsdiff>
  27896. <svntag>
  27897. <summary>Set SVN Release Tag</summary>
  27898. <function>doSvnTag</function>
  27899. <shortcut>sv</shortcut>
  27900. <options>
  27901. <quiet>
  27902. <shortopt>q</shortopt>
  27903. <doc>Be quiet</doc>
  27904. </quiet>
  27905. <slide>
  27906. <shortopt>F</shortopt>
  27907. <doc>Move (slide) tag if it exists</doc>
  27908. </slide>
  27909. <delete>
  27910. <shortopt>d</shortopt>
  27911. <doc>Remove tag</doc>
  27912. </delete>
  27913. <dry-run>
  27914. <shortopt>n</shortopt>
  27915. <doc>Don&#039;t do anything, just pretend</doc>
  27916. </dry-run>
  27917. </options>
  27918. <doc>&lt;package.xml&gt; [files...]
  27919. Sets a SVN tag on all files in a package. Use this command after you have
  27920. packaged a distribution tarball with the &quot;package&quot; command to tag what
  27921. revisions of what files were in that release. If need to fix something
  27922. after running svntag once, but before the tarball is released to the public,
  27923. use the &quot;slide&quot; option to move the release tag.
  27924. to include files (such as a second package.xml, or tests not included in the
  27925. release), pass them as additional parameters.
  27926. </doc>
  27927. </svntag>
  27928. <cvstag>
  27929. <summary>Set CVS Release Tag</summary>
  27930. <function>doCvsTag</function>
  27931. <shortcut>ct</shortcut>
  27932. <options>
  27933. <quiet>
  27934. <shortopt>q</shortopt>
  27935. <doc>Be quiet</doc>
  27936. </quiet>
  27937. <reallyquiet>
  27938. <shortopt>Q</shortopt>
  27939. <doc>Be really quiet</doc>
  27940. </reallyquiet>
  27941. <slide>
  27942. <shortopt>F</shortopt>
  27943. <doc>Move (slide) tag if it exists</doc>
  27944. </slide>
  27945. <delete>
  27946. <shortopt>d</shortopt>
  27947. <doc>Remove tag</doc>
  27948. </delete>
  27949. <dry-run>
  27950. <shortopt>n</shortopt>
  27951. <doc>Don&#039;t do anything, just pretend</doc>
  27952. </dry-run>
  27953. </options>
  27954. <doc>&lt;package.xml&gt; [files...]
  27955. Sets a CVS tag on all files in a package. Use this command after you have
  27956. packaged a distribution tarball with the &quot;package&quot; command to tag what
  27957. revisions of what files were in that release. If need to fix something
  27958. after running cvstag once, but before the tarball is released to the public,
  27959. use the &quot;slide&quot; option to move the release tag.
  27960. to include files (such as a second package.xml, or tests not included in the
  27961. release), pass them as additional parameters.
  27962. </doc>
  27963. </cvstag>
  27964. <package-dependencies>
  27965. <summary>Show package dependencies</summary>
  27966. <function>doPackageDependencies</function>
  27967. <shortcut>pd</shortcut>
  27968. <options />
  27969. <doc>&lt;package-file&gt; or &lt;package.xml&gt; or &lt;install-package-name&gt;
  27970. List all dependencies the package has.
  27971. Can take a tgz / tar file, package.xml or a package name of an installed package.</doc>
  27972. </package-dependencies>
  27973. <sign>
  27974. <summary>Sign a package distribution file</summary>
  27975. <function>doSign</function>
  27976. <shortcut>si</shortcut>
  27977. <options>
  27978. <verbose>
  27979. <shortopt>v</shortopt>
  27980. <doc>Display GnuPG output</doc>
  27981. </verbose>
  27982. </options>
  27983. <doc>&lt;package-file&gt;
  27984. Signs a package distribution (.tar or .tgz) file with GnuPG.</doc>
  27985. </sign>
  27986. <makerpm>
  27987. <summary>Builds an RPM spec file from a PEAR package</summary>
  27988. <function>doMakeRPM</function>
  27989. <shortcut>rpm</shortcut>
  27990. <options>
  27991. <spec-template>
  27992. <shortopt>t</shortopt>
  27993. <doc>Use FILE as RPM spec file template</doc>
  27994. <arg>FILE</arg>
  27995. </spec-template>
  27996. <rpm-pkgname>
  27997. <shortopt>p</shortopt>
  27998. <doc>Use FORMAT as format string for RPM package name, %s is replaced
  27999. by the PEAR package name, defaults to &quot;PEAR::%s&quot;.</doc>
  28000. <arg>FORMAT</arg>
  28001. </rpm-pkgname>
  28002. </options>
  28003. <doc>&lt;package-file&gt;
  28004. Creates an RPM .spec file for wrapping a PEAR package inside an RPM
  28005. package. Intended to be used from the SPECS directory, with the PEAR
  28006. package tarball in the SOURCES directory:
  28007. $ pear makerpm ../SOURCES/Net_Socket-1.0.tgz
  28008. Wrote RPM spec file PEAR::Net_Geo-1.0.spec
  28009. $ rpm -bb PEAR::Net_Socket-1.0.spec
  28010. ...
  28011. Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm
  28012. </doc>
  28013. </makerpm>
  28014. <convert>
  28015. <summary>Convert a package.xml 1.0 to package.xml 2.0 format</summary>
  28016. <function>doConvert</function>
  28017. <shortcut>c2</shortcut>
  28018. <options>
  28019. <flat>
  28020. <shortopt>f</shortopt>
  28021. <doc>do not beautify the filelist.</doc>
  28022. </flat>
  28023. </options>
  28024. <doc>[descfile] [descfile2]
  28025. Converts a package.xml in 1.0 format into a package.xml
  28026. in 2.0 format. The new file will be named package2.xml by default,
  28027. and package.xml will be used as the old file by default.
  28028. This is not the most intelligent conversion, and should only be
  28029. used for automated conversion or learning the format.
  28030. </doc>
  28031. </convert>
  28032. </commands>��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Package.php���������������������������������������������������������������0000664�0001750�0001750�00000116334�14720722517�016451� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  28033. /**
  28034. * PEAR_Command_Package (package, package-validate, cvsdiff, cvstag, package-dependencies,
  28035. * sign, makerpm, convert commands)
  28036. *
  28037. * PHP versions 4 and 5
  28038. *
  28039. * @category pear
  28040. * @package PEAR
  28041. * @author Stig Bakken <ssb@php.net>
  28042. * @author Martin Jansen <mj@php.net>
  28043. * @author Greg Beaver <cellog@php.net>
  28044. * @copyright 1997-2009 The Authors
  28045. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  28046. * @link http://pear.php.net/package/PEAR
  28047. * @since File available since Release 0.1
  28048. */
  28049. /**
  28050. * base class
  28051. */
  28052. require_once 'PEAR/Command/Common.php';
  28053. /**
  28054. * PEAR commands for login/logout
  28055. *
  28056. * @category pear
  28057. * @package PEAR
  28058. * @author Stig Bakken <ssb@php.net>
  28059. * @author Martin Jansen <mj@php.net>
  28060. * @author Greg Beaver <cellog@php.net>
  28061. * @copyright 1997-2009 The Authors
  28062. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  28063. * @version Release: @package_version@
  28064. * @link http://pear.php.net/package/PEAR
  28065. * @since Class available since Release 0.1
  28066. */
  28067. class PEAR_Command_Package extends PEAR_Command_Common
  28068. {
  28069. var $commands = array(
  28070. 'package' => array(
  28071. 'summary' => 'Build Package',
  28072. 'function' => 'doPackage',
  28073. 'shortcut' => 'p',
  28074. 'options' => array(
  28075. 'nocompress' => array(
  28076. 'shortopt' => 'Z',
  28077. 'doc' => 'Do not gzip the package file'
  28078. ),
  28079. 'showname' => array(
  28080. 'shortopt' => 'n',
  28081. 'doc' => 'Print the name of the packaged file.',
  28082. ),
  28083. ),
  28084. 'doc' => '[descfile] [descfile2]
  28085. Creates a PEAR package from its description file (usually called
  28086. package.xml). If a second packagefile is passed in, then
  28087. the packager will check to make sure that one is a package.xml
  28088. version 1.0, and the other is a package.xml version 2.0. The
  28089. package.xml version 1.0 will be saved as "package.xml" in the archive,
  28090. and the other as "package2.xml" in the archive"
  28091. '
  28092. ),
  28093. 'package-validate' => array(
  28094. 'summary' => 'Validate Package Consistency',
  28095. 'function' => 'doPackageValidate',
  28096. 'shortcut' => 'pv',
  28097. 'options' => array(),
  28098. 'doc' => '
  28099. ',
  28100. ),
  28101. 'cvsdiff' => array(
  28102. 'summary' => 'Run a "cvs diff" for all files in a package',
  28103. 'function' => 'doCvsDiff',
  28104. 'shortcut' => 'cd',
  28105. 'options' => array(
  28106. 'quiet' => array(
  28107. 'shortopt' => 'q',
  28108. 'doc' => 'Be quiet',
  28109. ),
  28110. 'reallyquiet' => array(
  28111. 'shortopt' => 'Q',
  28112. 'doc' => 'Be really quiet',
  28113. ),
  28114. 'date' => array(
  28115. 'shortopt' => 'D',
  28116. 'doc' => 'Diff against revision of DATE',
  28117. 'arg' => 'DATE',
  28118. ),
  28119. 'release' => array(
  28120. 'shortopt' => 'R',
  28121. 'doc' => 'Diff against tag for package release REL',
  28122. 'arg' => 'REL',
  28123. ),
  28124. 'revision' => array(
  28125. 'shortopt' => 'r',
  28126. 'doc' => 'Diff against revision REV',
  28127. 'arg' => 'REV',
  28128. ),
  28129. 'context' => array(
  28130. 'shortopt' => 'c',
  28131. 'doc' => 'Generate context diff',
  28132. ),
  28133. 'unified' => array(
  28134. 'shortopt' => 'u',
  28135. 'doc' => 'Generate unified diff',
  28136. ),
  28137. 'ignore-case' => array(
  28138. 'shortopt' => 'i',
  28139. 'doc' => 'Ignore case, consider upper- and lower-case letters equivalent',
  28140. ),
  28141. 'ignore-whitespace' => array(
  28142. 'shortopt' => 'b',
  28143. 'doc' => 'Ignore changes in amount of white space',
  28144. ),
  28145. 'ignore-blank-lines' => array(
  28146. 'shortopt' => 'B',
  28147. 'doc' => 'Ignore changes that insert or delete blank lines',
  28148. ),
  28149. 'brief' => array(
  28150. 'doc' => 'Report only whether the files differ, no details',
  28151. ),
  28152. 'dry-run' => array(
  28153. 'shortopt' => 'n',
  28154. 'doc' => 'Don\'t do anything, just pretend',
  28155. ),
  28156. ),
  28157. 'doc' => '<package.xml>
  28158. Compares all the files in a package. Without any options, this
  28159. command will compare the current code with the last checked-in code.
  28160. Using the -r or -R option you may compare the current code with that
  28161. of a specific release.
  28162. ',
  28163. ),
  28164. 'svntag' => array(
  28165. 'summary' => 'Set SVN Release Tag',
  28166. 'function' => 'doSvnTag',
  28167. 'shortcut' => 'sv',
  28168. 'options' => array(
  28169. 'quiet' => array(
  28170. 'shortopt' => 'q',
  28171. 'doc' => 'Be quiet',
  28172. ),
  28173. 'slide' => array(
  28174. 'shortopt' => 'F',
  28175. 'doc' => 'Move (slide) tag if it exists',
  28176. ),
  28177. 'delete' => array(
  28178. 'shortopt' => 'd',
  28179. 'doc' => 'Remove tag',
  28180. ),
  28181. 'dry-run' => array(
  28182. 'shortopt' => 'n',
  28183. 'doc' => 'Don\'t do anything, just pretend',
  28184. ),
  28185. ),
  28186. 'doc' => '<package.xml> [files...]
  28187. Sets a SVN tag on all files in a package. Use this command after you have
  28188. packaged a distribution tarball with the "package" command to tag what
  28189. revisions of what files were in that release. If need to fix something
  28190. after running svntag once, but before the tarball is released to the public,
  28191. use the "slide" option to move the release tag.
  28192. to include files (such as a second package.xml, or tests not included in the
  28193. release), pass them as additional parameters.
  28194. ',
  28195. ),
  28196. 'cvstag' => array(
  28197. 'summary' => 'Set CVS Release Tag',
  28198. 'function' => 'doCvsTag',
  28199. 'shortcut' => 'ct',
  28200. 'options' => array(
  28201. 'quiet' => array(
  28202. 'shortopt' => 'q',
  28203. 'doc' => 'Be quiet',
  28204. ),
  28205. 'reallyquiet' => array(
  28206. 'shortopt' => 'Q',
  28207. 'doc' => 'Be really quiet',
  28208. ),
  28209. 'slide' => array(
  28210. 'shortopt' => 'F',
  28211. 'doc' => 'Move (slide) tag if it exists',
  28212. ),
  28213. 'delete' => array(
  28214. 'shortopt' => 'd',
  28215. 'doc' => 'Remove tag',
  28216. ),
  28217. 'dry-run' => array(
  28218. 'shortopt' => 'n',
  28219. 'doc' => 'Don\'t do anything, just pretend',
  28220. ),
  28221. ),
  28222. 'doc' => '<package.xml> [files...]
  28223. Sets a CVS tag on all files in a package. Use this command after you have
  28224. packaged a distribution tarball with the "package" command to tag what
  28225. revisions of what files were in that release. If need to fix something
  28226. after running cvstag once, but before the tarball is released to the public,
  28227. use the "slide" option to move the release tag.
  28228. to include files (such as a second package.xml, or tests not included in the
  28229. release), pass them as additional parameters.
  28230. ',
  28231. ),
  28232. 'package-dependencies' => array(
  28233. 'summary' => 'Show package dependencies',
  28234. 'function' => 'doPackageDependencies',
  28235. 'shortcut' => 'pd',
  28236. 'options' => array(),
  28237. 'doc' => '<package-file> or <package.xml> or <install-package-name>
  28238. List all dependencies the package has.
  28239. Can take a tgz / tar file, package.xml or a package name of an installed package.'
  28240. ),
  28241. 'sign' => array(
  28242. 'summary' => 'Sign a package distribution file',
  28243. 'function' => 'doSign',
  28244. 'shortcut' => 'si',
  28245. 'options' => array(
  28246. 'verbose' => array(
  28247. 'shortopt' => 'v',
  28248. 'doc' => 'Display GnuPG output',
  28249. ),
  28250. ),
  28251. 'doc' => '<package-file>
  28252. Signs a package distribution (.tar or .tgz) file with GnuPG.',
  28253. ),
  28254. 'makerpm' => array(
  28255. 'summary' => 'Builds an RPM spec file from a PEAR package',
  28256. 'function' => 'doMakeRPM',
  28257. 'shortcut' => 'rpm',
  28258. 'options' => array(
  28259. 'spec-template' => array(
  28260. 'shortopt' => 't',
  28261. 'arg' => 'FILE',
  28262. 'doc' => 'Use FILE as RPM spec file template'
  28263. ),
  28264. 'rpm-pkgname' => array(
  28265. 'shortopt' => 'p',
  28266. 'arg' => 'FORMAT',
  28267. 'doc' => 'Use FORMAT as format string for RPM package name, %s is replaced
  28268. by the PEAR package name, defaults to "PEAR::%s".',
  28269. ),
  28270. ),
  28271. 'doc' => '<package-file>
  28272. Creates an RPM .spec file for wrapping a PEAR package inside an RPM
  28273. package. Intended to be used from the SPECS directory, with the PEAR
  28274. package tarball in the SOURCES directory:
  28275. $ pear makerpm ../SOURCES/Net_Socket-1.0.tgz
  28276. Wrote RPM spec file PEAR::Net_Geo-1.0.spec
  28277. $ rpm -bb PEAR::Net_Socket-1.0.spec
  28278. ...
  28279. Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm
  28280. ',
  28281. ),
  28282. 'convert' => array(
  28283. 'summary' => 'Convert a package.xml 1.0 to package.xml 2.0 format',
  28284. 'function' => 'doConvert',
  28285. 'shortcut' => 'c2',
  28286. 'options' => array(
  28287. 'flat' => array(
  28288. 'shortopt' => 'f',
  28289. 'doc' => 'do not beautify the filelist.',
  28290. ),
  28291. ),
  28292. 'doc' => '[descfile] [descfile2]
  28293. Converts a package.xml in 1.0 format into a package.xml
  28294. in 2.0 format. The new file will be named package2.xml by default,
  28295. and package.xml will be used as the old file by default.
  28296. This is not the most intelligent conversion, and should only be
  28297. used for automated conversion or learning the format.
  28298. '
  28299. ),
  28300. );
  28301. var $output;
  28302. /**
  28303. * PEAR_Command_Package constructor.
  28304. *
  28305. * @access public
  28306. */
  28307. function __construct(&$ui, &$config)
  28308. {
  28309. parent::__construct($ui, $config);
  28310. }
  28311. function _displayValidationResults($err, $warn, $strict = false)
  28312. {
  28313. foreach ($err as $e) {
  28314. $this->output .= "Error: $e\n";
  28315. }
  28316. foreach ($warn as $w) {
  28317. $this->output .= "Warning: $w\n";
  28318. }
  28319. $this->output .= sprintf('Validation: %d error(s), %d warning(s)'."\n",
  28320. sizeof($err), sizeof($warn));
  28321. if ($strict && count($err) > 0) {
  28322. $this->output .= "Fix these errors and try again.";
  28323. return false;
  28324. }
  28325. return true;
  28326. }
  28327. function &getPackager()
  28328. {
  28329. if (!class_exists('PEAR_Packager')) {
  28330. require_once 'PEAR/Packager.php';
  28331. }
  28332. $a = new PEAR_Packager;
  28333. return $a;
  28334. }
  28335. function &getPackageFile($config, $debug = false)
  28336. {
  28337. if (!class_exists('PEAR_Common')) {
  28338. require_once 'PEAR/Common.php';
  28339. }
  28340. if (!class_exists('PEAR_PackageFile')) {
  28341. require_once 'PEAR/PackageFile.php';
  28342. }
  28343. $a = new PEAR_PackageFile($config, $debug);
  28344. $common = new PEAR_Common;
  28345. $common->ui = $this->ui;
  28346. $a->setLogger($common);
  28347. return $a;
  28348. }
  28349. function doPackage($command, $options, $params)
  28350. {
  28351. $this->output = '';
  28352. $pkginfofile = isset($params[0]) ? $params[0] : 'package.xml';
  28353. $pkg2 = isset($params[1]) ? $params[1] : null;
  28354. if (!$pkg2 && !isset($params[0]) && file_exists('package2.xml')) {
  28355. $pkg2 = 'package2.xml';
  28356. }
  28357. $packager = &$this->getPackager();
  28358. $compress = empty($options['nocompress']) ? true : false;
  28359. $result = $packager->package($pkginfofile, $compress, $pkg2);
  28360. if (PEAR::isError($result)) {
  28361. return $this->raiseError($result);
  28362. }
  28363. // Don't want output, only the package file name just created
  28364. if (isset($options['showname'])) {
  28365. $this->output = $result;
  28366. }
  28367. if ($this->output) {
  28368. $this->ui->outputData($this->output, $command);
  28369. }
  28370. return true;
  28371. }
  28372. function doPackageValidate($command, $options, $params)
  28373. {
  28374. $this->output = '';
  28375. if (count($params) < 1) {
  28376. $params[0] = 'package.xml';
  28377. }
  28378. $obj = &$this->getPackageFile($this->config, $this->_debug);
  28379. $obj->rawReturn();
  28380. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  28381. $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL);
  28382. if (PEAR::isError($info)) {
  28383. $info = $obj->fromPackageFile($params[0], PEAR_VALIDATE_NORMAL);
  28384. } else {
  28385. $archive = $info->getArchiveFile();
  28386. $tar = new Archive_Tar($archive);
  28387. $tar->extract(dirname($info->getPackageFile()));
  28388. $info->setPackageFile(dirname($info->getPackageFile()) . DIRECTORY_SEPARATOR .
  28389. $info->getPackage() . '-' . $info->getVersion() . DIRECTORY_SEPARATOR .
  28390. basename($info->getPackageFile()));
  28391. }
  28392. PEAR::staticPopErrorHandling();
  28393. if (PEAR::isError($info)) {
  28394. return $this->raiseError($info);
  28395. }
  28396. $valid = false;
  28397. if ($info->getPackagexmlVersion() == '2.0') {
  28398. if ($valid = $info->validate(PEAR_VALIDATE_NORMAL)) {
  28399. $info->flattenFileList();
  28400. $valid = $info->validate(PEAR_VALIDATE_PACKAGING);
  28401. }
  28402. } else {
  28403. $valid = $info->validate(PEAR_VALIDATE_PACKAGING);
  28404. }
  28405. $err = $warn = array();
  28406. if ($errors = $info->getValidationWarnings()) {
  28407. foreach ($errors as $error) {
  28408. if ($error['level'] == 'warning') {
  28409. $warn[] = $error['message'];
  28410. } else {
  28411. $err[] = $error['message'];
  28412. }
  28413. }
  28414. }
  28415. $this->_displayValidationResults($err, $warn);
  28416. $this->ui->outputData($this->output, $command);
  28417. return true;
  28418. }
  28419. function doSvnTag($command, $options, $params)
  28420. {
  28421. $this->output = '';
  28422. $_cmd = $command;
  28423. if (count($params) < 1) {
  28424. $help = $this->getHelp($command);
  28425. return $this->raiseError("$command: missing parameter: $help[0]");
  28426. }
  28427. $packageFile = realpath($params[0]);
  28428. $dir = dirname($packageFile);
  28429. $dir = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1);
  28430. $obj = &$this->getPackageFile($this->config, $this->_debug);
  28431. $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL);
  28432. if (PEAR::isError($info)) {
  28433. return $this->raiseError($info);
  28434. }
  28435. $err = $warn = array();
  28436. if (!$info->validate()) {
  28437. foreach ($info->getValidationWarnings() as $error) {
  28438. if ($error['level'] == 'warning') {
  28439. $warn[] = $error['message'];
  28440. } else {
  28441. $err[] = $error['message'];
  28442. }
  28443. }
  28444. }
  28445. if (!$this->_displayValidationResults($err, $warn, true)) {
  28446. $this->ui->outputData($this->output, $command);
  28447. return $this->raiseError('SVN tag failed');
  28448. }
  28449. $version = $info->getVersion();
  28450. $package = $info->getName();
  28451. $svntag = "$package-$version";
  28452. if (isset($options['delete'])) {
  28453. return $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options);
  28454. }
  28455. $path = $this->_svnFindPath($packageFile);
  28456. // Check if there are any modified files
  28457. $fp = popen('svn st --xml ' . dirname($packageFile), "r");
  28458. $out = '';
  28459. while ($line = fgets($fp, 1024)) {
  28460. $out .= rtrim($line)."\n";
  28461. }
  28462. pclose($fp);
  28463. if (!isset($options['quiet']) && strpos($out, 'item="modified"')) {
  28464. $params = array(array(
  28465. 'name' => 'modified',
  28466. 'type' => 'yesno',
  28467. 'default' => 'no',
  28468. 'prompt' => 'You have files in your SVN checkout (' . $path['from'] . ') that have been modified but not committed, do you still want to tag ' . $version . '?',
  28469. ));
  28470. $answers = $this->ui->confirmDialog($params);
  28471. if (!in_array($answers['modified'], array('y', 'yes', 'on', '1'))) {
  28472. return true;
  28473. }
  28474. }
  28475. if (isset($options['slide'])) {
  28476. $this->_svnRemoveTag($version, $package, $svntag, $packageFile, $options);
  28477. }
  28478. // Check if tag already exists
  28479. $releaseTag = $path['local']['base'] . 'tags' . DIRECTORY_SEPARATOR . $svntag;
  28480. $existsCommand = 'svn ls ' . $path['base'] . 'tags/';
  28481. $fp = popen($existsCommand, "r");
  28482. $out = '';
  28483. while ($line = fgets($fp, 1024)) {
  28484. $out .= rtrim($line)."\n";
  28485. }
  28486. pclose($fp);
  28487. if (in_array($svntag . DIRECTORY_SEPARATOR, explode("\n", $out))) {
  28488. $this->ui->outputData($this->output, $command);
  28489. return $this->raiseError('SVN tag ' . $svntag . ' for ' . $package . ' already exists.');
  28490. } elseif (file_exists($path['local']['base'] . 'tags') === false) {
  28491. return $this->raiseError('Can not locate the tags directory at ' . $path['local']['base'] . 'tags');
  28492. } elseif (is_writeable($path['local']['base'] . 'tags') === false) {
  28493. return $this->raiseError('Can not write to the tag directory at ' . $path['local']['base'] . 'tags');
  28494. } else {
  28495. $makeCommand = 'svn mkdir ' . $releaseTag;
  28496. $this->output .= "+ $makeCommand\n";
  28497. if (empty($options['dry-run'])) {
  28498. // We need to create the tag dir.
  28499. $fp = popen($makeCommand, "r");
  28500. $out = '';
  28501. while ($line = fgets($fp, 1024)) {
  28502. $out .= rtrim($line)."\n";
  28503. }
  28504. pclose($fp);
  28505. $this->output .= "$out\n";
  28506. }
  28507. }
  28508. $command = 'svn';
  28509. if (isset($options['quiet'])) {
  28510. $command .= ' -q';
  28511. }
  28512. $command .= ' copy --parents ';
  28513. $dir = dirname($packageFile);
  28514. $dir = substr($dir, strrpos($dir, DIRECTORY_SEPARATOR) + 1);
  28515. $files = array_keys($info->getFilelist());
  28516. if (!in_array(basename($packageFile), $files)) {
  28517. $files[] = basename($packageFile);
  28518. }
  28519. array_shift($params);
  28520. if (count($params)) {
  28521. // add in additional files to be tagged (package files and such)
  28522. $files = array_merge($files, $params);
  28523. }
  28524. $commands = array();
  28525. foreach ($files as $file) {
  28526. if (!file_exists($file)) {
  28527. $file = $dir . DIRECTORY_SEPARATOR . $file;
  28528. }
  28529. $commands[] = $command . ' ' . escapeshellarg($file) . ' ' .
  28530. escapeshellarg($releaseTag . DIRECTORY_SEPARATOR . $file);
  28531. }
  28532. $this->output .= implode("\n", $commands) . "\n";
  28533. if (empty($options['dry-run'])) {
  28534. foreach ($commands as $command) {
  28535. $fp = popen($command, "r");
  28536. while ($line = fgets($fp, 1024)) {
  28537. $this->output .= rtrim($line)."\n";
  28538. }
  28539. pclose($fp);
  28540. }
  28541. }
  28542. $command = 'svn ci -m "Tagging the ' . $version . ' release" ' . $releaseTag . "\n";
  28543. $this->output .= "+ $command\n";
  28544. if (empty($options['dry-run'])) {
  28545. $fp = popen($command, "r");
  28546. while ($line = fgets($fp, 1024)) {
  28547. $this->output .= rtrim($line)."\n";
  28548. }
  28549. pclose($fp);
  28550. }
  28551. $this->ui->outputData($this->output, $_cmd);
  28552. return true;
  28553. }
  28554. function _svnFindPath($file)
  28555. {
  28556. $xml = '';
  28557. $command = "svn info --xml $file";
  28558. $fp = popen($command, "r");
  28559. while ($line = fgets($fp, 1024)) {
  28560. $xml .= rtrim($line)."\n";
  28561. }
  28562. pclose($fp);
  28563. $url_tag = strpos($xml, '<url>');
  28564. $url = substr($xml, $url_tag + 5, strpos($xml, '</url>', $url_tag + 5) - ($url_tag + 5));
  28565. $path = array();
  28566. $path['from'] = substr($url, 0, strrpos($url, '/'));
  28567. $path['base'] = substr($path['from'], 0, strrpos($path['from'], '/') + 1);
  28568. // Figure out the local paths - see http://pear.php.net/bugs/17463
  28569. $pos = strpos($file, DIRECTORY_SEPARATOR . 'trunk' . DIRECTORY_SEPARATOR);
  28570. if ($pos === false) {
  28571. $pos = strpos($file, DIRECTORY_SEPARATOR . 'branches' . DIRECTORY_SEPARATOR);
  28572. }
  28573. $path['local']['base'] = substr($file, 0, $pos + 1);
  28574. return $path;
  28575. }
  28576. function _svnRemoveTag($version, $package, $tag, $packageFile, $options)
  28577. {
  28578. $command = 'svn';
  28579. if (isset($options['quiet'])) {
  28580. $command .= ' -q';
  28581. }
  28582. $command .= ' remove';
  28583. $command .= ' -m "Removing tag for the ' . $version . ' release."';
  28584. $path = $this->_svnFindPath($packageFile);
  28585. $command .= ' ' . $path['base'] . 'tags/' . $tag;
  28586. if ($this->config->get('verbose') > 1) {
  28587. $this->output .= "+ $command\n";
  28588. }
  28589. $this->output .= "+ $command\n";
  28590. if (empty($options['dry-run'])) {
  28591. $fp = popen($command, "r");
  28592. while ($line = fgets($fp, 1024)) {
  28593. $this->output .= rtrim($line)."\n";
  28594. }
  28595. pclose($fp);
  28596. }
  28597. $this->ui->outputData($this->output, $command);
  28598. return true;
  28599. }
  28600. function doCvsTag($command, $options, $params)
  28601. {
  28602. $this->output = '';
  28603. $_cmd = $command;
  28604. if (count($params) < 1) {
  28605. $help = $this->getHelp($command);
  28606. return $this->raiseError("$command: missing parameter: $help[0]");
  28607. }
  28608. $packageFile = realpath($params[0]);
  28609. $obj = &$this->getPackageFile($this->config, $this->_debug);
  28610. $info = $obj->fromAnyFile($packageFile, PEAR_VALIDATE_NORMAL);
  28611. if (PEAR::isError($info)) {
  28612. return $this->raiseError($info);
  28613. }
  28614. $err = $warn = array();
  28615. if (!$info->validate()) {
  28616. foreach ($info->getValidationWarnings() as $error) {
  28617. if ($error['level'] == 'warning') {
  28618. $warn[] = $error['message'];
  28619. } else {
  28620. $err[] = $error['message'];
  28621. }
  28622. }
  28623. }
  28624. if (!$this->_displayValidationResults($err, $warn, true)) {
  28625. $this->ui->outputData($this->output, $command);
  28626. return $this->raiseError('CVS tag failed');
  28627. }
  28628. $version = $info->getVersion();
  28629. $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $version);
  28630. $cvstag = "RELEASE_$cvsversion";
  28631. $files = array_keys($info->getFilelist());
  28632. $command = 'cvs';
  28633. if (isset($options['quiet'])) {
  28634. $command .= ' -q';
  28635. }
  28636. if (isset($options['reallyquiet'])) {
  28637. $command .= ' -Q';
  28638. }
  28639. $command .= ' tag';
  28640. if (isset($options['slide'])) {
  28641. $command .= ' -F';
  28642. }
  28643. if (isset($options['delete'])) {
  28644. $command .= ' -d';
  28645. }
  28646. $command .= ' ' . $cvstag . ' ' . escapeshellarg($params[0]);
  28647. array_shift($params);
  28648. if (count($params)) {
  28649. // add in additional files to be tagged
  28650. $files = array_merge($files, $params);
  28651. }
  28652. $dir = dirname($packageFile);
  28653. $dir = substr($dir, strrpos($dir, '/') + 1);
  28654. foreach ($files as $file) {
  28655. if (!file_exists($file)) {
  28656. $file = $dir . DIRECTORY_SEPARATOR . $file;
  28657. }
  28658. $command .= ' ' . escapeshellarg($file);
  28659. }
  28660. if ($this->config->get('verbose') > 1) {
  28661. $this->output .= "+ $command\n";
  28662. }
  28663. $this->output .= "+ $command\n";
  28664. if (empty($options['dry-run'])) {
  28665. $fp = popen($command, "r");
  28666. while ($line = fgets($fp, 1024)) {
  28667. $this->output .= rtrim($line)."\n";
  28668. }
  28669. pclose($fp);
  28670. }
  28671. $this->ui->outputData($this->output, $_cmd);
  28672. return true;
  28673. }
  28674. function doCvsDiff($command, $options, $params)
  28675. {
  28676. $this->output = '';
  28677. if (sizeof($params) < 1) {
  28678. $help = $this->getHelp($command);
  28679. return $this->raiseError("$command: missing parameter: $help[0]");
  28680. }
  28681. $file = realpath($params[0]);
  28682. $obj = &$this->getPackageFile($this->config, $this->_debug);
  28683. $info = $obj->fromAnyFile($file, PEAR_VALIDATE_NORMAL);
  28684. if (PEAR::isError($info)) {
  28685. return $this->raiseError($info);
  28686. }
  28687. $err = $warn = array();
  28688. if (!$info->validate()) {
  28689. foreach ($info->getValidationWarnings() as $error) {
  28690. if ($error['level'] == 'warning') {
  28691. $warn[] = $error['message'];
  28692. } else {
  28693. $err[] = $error['message'];
  28694. }
  28695. }
  28696. }
  28697. if (!$this->_displayValidationResults($err, $warn, true)) {
  28698. $this->ui->outputData($this->output, $command);
  28699. return $this->raiseError('CVS diff failed');
  28700. }
  28701. $info1 = $info->getFilelist();
  28702. $files = $info1;
  28703. $cmd = "cvs";
  28704. if (isset($options['quiet'])) {
  28705. $cmd .= ' -q';
  28706. unset($options['quiet']);
  28707. }
  28708. if (isset($options['reallyquiet'])) {
  28709. $cmd .= ' -Q';
  28710. unset($options['reallyquiet']);
  28711. }
  28712. if (isset($options['release'])) {
  28713. $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $options['release']);
  28714. $cvstag = "RELEASE_$cvsversion";
  28715. $options['revision'] = $cvstag;
  28716. unset($options['release']);
  28717. }
  28718. $execute = true;
  28719. if (isset($options['dry-run'])) {
  28720. $execute = false;
  28721. unset($options['dry-run']);
  28722. }
  28723. $cmd .= ' diff';
  28724. // the rest of the options are passed right on to "cvs diff"
  28725. foreach ($options as $option => $optarg) {
  28726. $arg = $short = false;
  28727. if (isset($this->commands[$command]['options'][$option])) {
  28728. $arg = $this->commands[$command]['options'][$option]['arg'];
  28729. $short = $this->commands[$command]['options'][$option]['shortopt'];
  28730. }
  28731. $cmd .= $short ? " -$short" : " --$option";
  28732. if ($arg && $optarg) {
  28733. $cmd .= ($short ? '' : '=') . escapeshellarg($optarg);
  28734. }
  28735. }
  28736. foreach ($files as $file) {
  28737. $cmd .= ' ' . escapeshellarg($file['name']);
  28738. }
  28739. if ($this->config->get('verbose') > 1) {
  28740. $this->output .= "+ $cmd\n";
  28741. }
  28742. if ($execute) {
  28743. $fp = popen($cmd, "r");
  28744. while ($line = fgets($fp, 1024)) {
  28745. $this->output .= rtrim($line)."\n";
  28746. }
  28747. pclose($fp);
  28748. }
  28749. $this->ui->outputData($this->output, $command);
  28750. return true;
  28751. }
  28752. function doPackageDependencies($command, $options, $params)
  28753. {
  28754. // $params[0] -> the PEAR package to list its information
  28755. if (count($params) !== 1) {
  28756. return $this->raiseError("bad parameter(s), try \"help $command\"");
  28757. }
  28758. $obj = &$this->getPackageFile($this->config, $this->_debug);
  28759. if (is_file($params[0]) || strpos($params[0], '.xml') > 0) {
  28760. $info = $obj->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL);
  28761. } else {
  28762. $reg = $this->config->getRegistry();
  28763. $info = $obj->fromArray($reg->packageInfo($params[0]));
  28764. }
  28765. if (PEAR::isError($info)) {
  28766. return $this->raiseError($info);
  28767. }
  28768. $deps = $info->getDeps();
  28769. if (is_array($deps)) {
  28770. if ($info->getPackagexmlVersion() == '1.0') {
  28771. $data = array(
  28772. 'caption' => 'Dependencies for pear/' . $info->getPackage(),
  28773. 'border' => true,
  28774. 'headline' => array("Required?", "Type", "Name", "Relation", "Version"),
  28775. );
  28776. foreach ($deps as $d) {
  28777. if (isset($d['optional'])) {
  28778. if ($d['optional'] == 'yes') {
  28779. $req = 'No';
  28780. } else {
  28781. $req = 'Yes';
  28782. }
  28783. } else {
  28784. $req = 'Yes';
  28785. }
  28786. if (isset($this->_deps_rel_trans[$d['rel']])) {
  28787. $rel = $this->_deps_rel_trans[$d['rel']];
  28788. } else {
  28789. $rel = $d['rel'];
  28790. }
  28791. if (isset($this->_deps_type_trans[$d['type']])) {
  28792. $type = ucfirst($this->_deps_type_trans[$d['type']]);
  28793. } else {
  28794. $type = $d['type'];
  28795. }
  28796. if (isset($d['name'])) {
  28797. $name = $d['name'];
  28798. } else {
  28799. $name = '';
  28800. }
  28801. if (isset($d['version'])) {
  28802. $version = $d['version'];
  28803. } else {
  28804. $version = '';
  28805. }
  28806. $data['data'][] = array($req, $type, $name, $rel, $version);
  28807. }
  28808. } else { // package.xml 2.0 dependencies display
  28809. require_once 'PEAR/Dependency2.php';
  28810. $deps = $info->getDependencies();
  28811. $reg = &$this->config->getRegistry();
  28812. if (is_array($deps)) {
  28813. $data = array(
  28814. 'caption' => 'Dependencies for ' . $info->getPackage(),
  28815. 'border' => true,
  28816. 'headline' => array("Required?", "Type", "Name", 'Versioning', 'Group'),
  28817. );
  28818. foreach ($deps as $type => $subd) {
  28819. $req = ($type == 'required') ? 'Yes' : 'No';
  28820. if ($type == 'group' && isset($subd['attribs']['name'])) {
  28821. $group = $subd['attribs']['name'];
  28822. } else {
  28823. $group = '';
  28824. }
  28825. if (!isset($subd[0])) {
  28826. $subd = array($subd);
  28827. }
  28828. foreach ($subd as $groupa) {
  28829. foreach ($groupa as $deptype => $depinfo) {
  28830. if ($deptype == 'attribs') {
  28831. continue;
  28832. }
  28833. if ($deptype == 'pearinstaller') {
  28834. $deptype = 'pear Installer';
  28835. }
  28836. if (!isset($depinfo[0])) {
  28837. $depinfo = array($depinfo);
  28838. }
  28839. foreach ($depinfo as $inf) {
  28840. $name = '';
  28841. if (isset($inf['channel'])) {
  28842. $alias = $reg->channelAlias($inf['channel']);
  28843. if (!$alias) {
  28844. $alias = '(channel?) ' .$inf['channel'];
  28845. }
  28846. $name = $alias . '/';
  28847. }
  28848. if (isset($inf['name'])) {
  28849. $name .= $inf['name'];
  28850. } elseif (isset($inf['pattern'])) {
  28851. $name .= $inf['pattern'];
  28852. } else {
  28853. $name .= '';
  28854. }
  28855. if (isset($inf['uri'])) {
  28856. $name .= ' [' . $inf['uri'] . ']';
  28857. }
  28858. if (isset($inf['conflicts'])) {
  28859. $ver = 'conflicts';
  28860. } else {
  28861. $ver = PEAR_Dependency2::_getExtraString($inf);
  28862. }
  28863. $data['data'][] = array($req, ucfirst($deptype), $name,
  28864. $ver, $group);
  28865. }
  28866. }
  28867. }
  28868. }
  28869. }
  28870. }
  28871. $this->ui->outputData($data, $command);
  28872. return true;
  28873. }
  28874. // Fallback
  28875. $this->ui->outputData("This package does not have any dependencies.", $command);
  28876. }
  28877. function doSign($command, $options, $params)
  28878. {
  28879. // should move most of this code into PEAR_Packager
  28880. // so it'll be easy to implement "pear package --sign"
  28881. if (count($params) !== 1) {
  28882. return $this->raiseError("bad parameter(s), try \"help $command\"");
  28883. }
  28884. require_once 'System.php';
  28885. require_once 'Archive/Tar.php';
  28886. if (!file_exists($params[0])) {
  28887. return $this->raiseError("file does not exist: $params[0]");
  28888. }
  28889. $obj = $this->getPackageFile($this->config, $this->_debug);
  28890. $info = $obj->fromTgzFile($params[0], PEAR_VALIDATE_NORMAL);
  28891. if (PEAR::isError($info)) {
  28892. return $this->raiseError($info);
  28893. }
  28894. $tar = new Archive_Tar($params[0]);
  28895. $tmpdir = $this->config->get('temp_dir');
  28896. $tmpdir = System::mktemp(' -t "' . $tmpdir . '" -d pearsign');
  28897. if (!$tar->extractList('package2.xml package.xml package.sig', $tmpdir)) {
  28898. return $this->raiseError("failed to extract tar file");
  28899. }
  28900. if (file_exists("$tmpdir/package.sig")) {
  28901. return $this->raiseError("package already signed");
  28902. }
  28903. $packagexml = 'package.xml';
  28904. if (file_exists("$tmpdir/package2.xml")) {
  28905. $packagexml = 'package2.xml';
  28906. }
  28907. if (file_exists("$tmpdir/package.sig")) {
  28908. unlink("$tmpdir/package.sig");
  28909. }
  28910. if (!file_exists("$tmpdir/$packagexml")) {
  28911. return $this->raiseError("Extracted file $tmpdir/$packagexml not found.");
  28912. }
  28913. $input = $this->ui->userDialog($command,
  28914. array('GnuPG Passphrase'),
  28915. array('password'));
  28916. if (!isset($input[0])) {
  28917. //use empty passphrase
  28918. $input[0] = '';
  28919. }
  28920. $devnull = (isset($options['verbose'])) ? '' : ' 2>/dev/null';
  28921. $gpg = popen("gpg --batch --passphrase-fd 0 --armor --detach-sign --output $tmpdir/package.sig $tmpdir/$packagexml" . $devnull, "w");
  28922. if (!$gpg) {
  28923. return $this->raiseError("gpg command failed");
  28924. }
  28925. fwrite($gpg, "$input[0]\n");
  28926. if (pclose($gpg) || !file_exists("$tmpdir/package.sig")) {
  28927. return $this->raiseError("gpg sign failed");
  28928. }
  28929. if (!$tar->addModify("$tmpdir/package.sig", '', $tmpdir)) {
  28930. return $this->raiseError('failed adding signature to file');
  28931. }
  28932. $this->ui->outputData("Package signed.", $command);
  28933. return true;
  28934. }
  28935. /**
  28936. * For unit testing purposes
  28937. */
  28938. function &getInstaller(&$ui)
  28939. {
  28940. if (!class_exists('PEAR_Installer')) {
  28941. require_once 'PEAR/Installer.php';
  28942. }
  28943. $a = new PEAR_Installer($ui);
  28944. return $a;
  28945. }
  28946. /**
  28947. * For unit testing purposes
  28948. */
  28949. function &getCommandPackaging(&$ui, &$config)
  28950. {
  28951. if (!class_exists('PEAR_Command_Packaging')) {
  28952. if ($fp = @fopen('PEAR/Command/Packaging.php', 'r', true)) {
  28953. fclose($fp);
  28954. include_once 'PEAR/Command/Packaging.php';
  28955. }
  28956. }
  28957. if (class_exists('PEAR_Command_Packaging')) {
  28958. $a = new PEAR_Command_Packaging($ui, $config);
  28959. } else {
  28960. $a = null;
  28961. }
  28962. return $a;
  28963. }
  28964. function doMakeRPM($command, $options, $params)
  28965. {
  28966. // Check to see if PEAR_Command_Packaging is installed, and
  28967. // transparently switch to use the "make-rpm-spec" command from it
  28968. // instead, if it does. Otherwise, continue to use the old version
  28969. // of "makerpm" supplied with this package (PEAR).
  28970. $packaging_cmd = $this->getCommandPackaging($this->ui, $this->config);
  28971. if ($packaging_cmd !== null) {
  28972. $this->ui->outputData('PEAR_Command_Packaging is installed; using '.
  28973. 'newer "make-rpm-spec" command instead');
  28974. return $packaging_cmd->run('make-rpm-spec', $options, $params);
  28975. }
  28976. $this->ui->outputData('WARNING: "pear makerpm" is no longer available; an '.
  28977. 'improved version is available via "pear make-rpm-spec", which '.
  28978. 'is available by installing PEAR_Command_Packaging');
  28979. return true;
  28980. }
  28981. function doConvert($command, $options, $params)
  28982. {
  28983. $packagexml = isset($params[0]) ? $params[0] : 'package.xml';
  28984. $newpackagexml = isset($params[1]) ? $params[1] : dirname($packagexml) .
  28985. DIRECTORY_SEPARATOR . 'package2.xml';
  28986. $pkg = &$this->getPackageFile($this->config, $this->_debug);
  28987. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  28988. $pf = $pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL);
  28989. PEAR::staticPopErrorHandling();
  28990. if (PEAR::isError($pf)) {
  28991. if (is_array($pf->getUserInfo())) {
  28992. foreach ($pf->getUserInfo() as $warning) {
  28993. $this->ui->outputData($warning['message']);
  28994. }
  28995. }
  28996. return $this->raiseError($pf);
  28997. }
  28998. if (is_a($pf, 'PEAR_PackageFile_v2')) {
  28999. $this->ui->outputData($packagexml . ' is already a package.xml version 2.0');
  29000. return true;
  29001. }
  29002. $gen = &$pf->getDefaultGenerator();
  29003. $newpf = &$gen->toV2();
  29004. $newpf->setPackagefile($newpackagexml);
  29005. $gen = &$newpf->getDefaultGenerator();
  29006. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  29007. $state = (isset($options['flat']) ? PEAR_VALIDATE_PACKAGING : PEAR_VALIDATE_NORMAL);
  29008. $saved = $gen->toPackageFile(dirname($newpackagexml), $state, basename($newpackagexml));
  29009. PEAR::staticPopErrorHandling();
  29010. if (PEAR::isError($saved)) {
  29011. if (is_array($saved->getUserInfo())) {
  29012. foreach ($saved->getUserInfo() as $warning) {
  29013. $this->ui->outputData($warning['message']);
  29014. }
  29015. }
  29016. $this->ui->outputData($saved->getMessage());
  29017. return true;
  29018. }
  29019. $this->ui->outputData('Wrote new version 2.0 package.xml to "' . $saved . '"');
  29020. return true;
  29021. }
  29022. }
  29023. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Pickle.xml����������������������������������������������������������������0000644�0001750�0001750�00000002233�14720722517�016324� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<commands version="1.0">
  29024. <pickle>
  29025. <summary>Build PECL Package</summary>
  29026. <function>doPackage</function>
  29027. <shortcut>pi</shortcut>
  29028. <options>
  29029. <nocompress>
  29030. <shortopt>Z</shortopt>
  29031. <doc>Do not gzip the package file</doc>
  29032. </nocompress>
  29033. <showname>
  29034. <shortopt>n</shortopt>
  29035. <doc>Print the name of the packaged file.</doc>
  29036. </showname>
  29037. </options>
  29038. <doc>[descfile]
  29039. Creates a PECL package from its package2.xml file.
  29040. An automatic conversion will be made to a package.xml 1.0 and written out to
  29041. disk in the current directory as &quot;package.xml&quot;. Note that
  29042. only simple package.xml 2.0 will be converted. package.xml 2.0 with:
  29043. - dependency types other than required/optional PECL package/ext/php/pearinstaller
  29044. - more than one extsrcrelease or zendextsrcrelease
  29045. - zendextbinrelease, extbinrelease, phprelease, or bundle release type
  29046. - dependency groups
  29047. - ignore tags in release filelist
  29048. - tasks other than replace
  29049. - custom roles
  29050. will cause pickle to fail, and output an error message. If your package2.xml
  29051. uses any of these features, you are best off using PEAR_PackageFileManager to
  29052. generate both package.xml.
  29053. </doc>
  29054. </pickle>
  29055. </commands>���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Pickle.php����������������������������������������������������������������0000644�0001750�0001750�00000037027�14720722517�016324� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  29056. /**
  29057. * PEAR_Command_Pickle (pickle command)
  29058. *
  29059. * PHP versions 4 and 5
  29060. *
  29061. * @category pear
  29062. * @package PEAR
  29063. * @author Greg Beaver <cellog@php.net>
  29064. * @copyright 2005-2009 The Authors
  29065. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  29066. * @link http://pear.php.net/package/PEAR
  29067. * @since File available since Release 1.4.1
  29068. */
  29069. /**
  29070. * base class
  29071. */
  29072. require_once 'PEAR/Command/Common.php';
  29073. /**
  29074. * PEAR commands for login/logout
  29075. *
  29076. * @category pear
  29077. * @package PEAR
  29078. * @author Greg Beaver <cellog@php.net>
  29079. * @copyright 2005-2009 The Authors
  29080. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  29081. * @version Release: 1.10.16
  29082. * @link http://pear.php.net/package/PEAR
  29083. * @since Class available since Release 1.4.1
  29084. */
  29085. class PEAR_Command_Pickle extends PEAR_Command_Common
  29086. {
  29087. var $commands = array(
  29088. 'pickle' => array(
  29089. 'summary' => 'Build PECL Package',
  29090. 'function' => 'doPackage',
  29091. 'shortcut' => 'pi',
  29092. 'options' => array(
  29093. 'nocompress' => array(
  29094. 'shortopt' => 'Z',
  29095. 'doc' => 'Do not gzip the package file'
  29096. ),
  29097. 'showname' => array(
  29098. 'shortopt' => 'n',
  29099. 'doc' => 'Print the name of the packaged file.',
  29100. ),
  29101. ),
  29102. 'doc' => '[descfile]
  29103. Creates a PECL package from its package2.xml file.
  29104. An automatic conversion will be made to a package.xml 1.0 and written out to
  29105. disk in the current directory as "package.xml". Note that
  29106. only simple package.xml 2.0 will be converted. package.xml 2.0 with:
  29107. - dependency types other than required/optional PECL package/ext/php/pearinstaller
  29108. - more than one extsrcrelease or zendextsrcrelease
  29109. - zendextbinrelease, extbinrelease, phprelease, or bundle release type
  29110. - dependency groups
  29111. - ignore tags in release filelist
  29112. - tasks other than replace
  29113. - custom roles
  29114. will cause pickle to fail, and output an error message. If your package2.xml
  29115. uses any of these features, you are best off using PEAR_PackageFileManager to
  29116. generate both package.xml.
  29117. '
  29118. ),
  29119. );
  29120. /**
  29121. * PEAR_Command_Package constructor.
  29122. *
  29123. * @access public
  29124. */
  29125. function __construct(&$ui, &$config)
  29126. {
  29127. parent::__construct($ui, $config);
  29128. }
  29129. /**
  29130. * For unit-testing ease
  29131. *
  29132. * @return PEAR_Packager
  29133. */
  29134. function &getPackager()
  29135. {
  29136. if (!class_exists('PEAR_Packager')) {
  29137. require_once 'PEAR/Packager.php';
  29138. }
  29139. $a = new PEAR_Packager;
  29140. return $a;
  29141. }
  29142. /**
  29143. * For unit-testing ease
  29144. *
  29145. * @param PEAR_Config $config
  29146. * @param bool $debug
  29147. * @param string|null $tmpdir
  29148. * @return PEAR_PackageFile
  29149. */
  29150. function &getPackageFile($config, $debug = false)
  29151. {
  29152. if (!class_exists('PEAR_Common')) {
  29153. require_once 'PEAR/Common.php';
  29154. }
  29155. if (!class_exists('PEAR_PackageFile')) {
  29156. require_once 'PEAR/PackageFile.php';
  29157. }
  29158. $a = new PEAR_PackageFile($config, $debug);
  29159. $common = new PEAR_Common;
  29160. $common->ui = $this->ui;
  29161. $a->setLogger($common);
  29162. return $a;
  29163. }
  29164. function doPackage($command, $options, $params)
  29165. {
  29166. $this->output = '';
  29167. $pkginfofile = isset($params[0]) ? $params[0] : 'package2.xml';
  29168. $packager = &$this->getPackager();
  29169. if (PEAR::isError($err = $this->_convertPackage($pkginfofile))) {
  29170. return $err;
  29171. }
  29172. $compress = empty($options['nocompress']) ? true : false;
  29173. $result = $packager->package($pkginfofile, $compress, 'package.xml');
  29174. if (PEAR::isError($result)) {
  29175. return $this->raiseError($result);
  29176. }
  29177. // Don't want output, only the package file name just created
  29178. if (isset($options['showname'])) {
  29179. $this->ui->outputData($result, $command);
  29180. }
  29181. return true;
  29182. }
  29183. function _convertPackage($packagexml)
  29184. {
  29185. $pkg = &$this->getPackageFile($this->config);
  29186. $pf2 = &$pkg->fromPackageFile($packagexml, PEAR_VALIDATE_NORMAL);
  29187. if (!is_a($pf2, 'PEAR_PackageFile_v2')) {
  29188. return $this->raiseError('Cannot process "' .
  29189. $packagexml . '", is not a package.xml 2.0');
  29190. }
  29191. require_once 'PEAR/PackageFile/v1.php';
  29192. $pf = new PEAR_PackageFile_v1;
  29193. $pf->setConfig($this->config);
  29194. if ($pf2->getPackageType() != 'extsrc' && $pf2->getPackageType() != 'zendextsrc') {
  29195. return $this->raiseError('Cannot safely convert "' . $packagexml .
  29196. '", is not an extension source package. Using a PEAR_PackageFileManager-based ' .
  29197. 'script is an option');
  29198. }
  29199. if (is_array($pf2->getUsesRole())) {
  29200. return $this->raiseError('Cannot safely convert "' . $packagexml .
  29201. '", contains custom roles. Using a PEAR_PackageFileManager-based script or ' .
  29202. 'the convert command is an option');
  29203. }
  29204. if (is_array($pf2->getUsesTask())) {
  29205. return $this->raiseError('Cannot safely convert "' . $packagexml .
  29206. '", contains custom tasks. Using a PEAR_PackageFileManager-based script or ' .
  29207. 'the convert command is an option');
  29208. }
  29209. $deps = $pf2->getDependencies();
  29210. if (isset($deps['group'])) {
  29211. return $this->raiseError('Cannot safely convert "' . $packagexml .
  29212. '", contains dependency groups. Using a PEAR_PackageFileManager-based script ' .
  29213. 'or the convert command is an option');
  29214. }
  29215. if (isset($deps['required']['subpackage']) ||
  29216. isset($deps['optional']['subpackage'])) {
  29217. return $this->raiseError('Cannot safely convert "' . $packagexml .
  29218. '", contains subpackage dependencies. Using a PEAR_PackageFileManager-based '.
  29219. 'script is an option');
  29220. }
  29221. if (isset($deps['required']['os'])) {
  29222. return $this->raiseError('Cannot safely convert "' . $packagexml .
  29223. '", contains os dependencies. Using a PEAR_PackageFileManager-based '.
  29224. 'script is an option');
  29225. }
  29226. if (isset($deps['required']['arch'])) {
  29227. return $this->raiseError('Cannot safely convert "' . $packagexml .
  29228. '", contains arch dependencies. Using a PEAR_PackageFileManager-based '.
  29229. 'script is an option');
  29230. }
  29231. $pf->setPackage($pf2->getPackage());
  29232. $pf->setSummary($pf2->getSummary());
  29233. $pf->setDescription($pf2->getDescription());
  29234. foreach ($pf2->getMaintainers() as $maintainer) {
  29235. $pf->addMaintainer($maintainer['role'], $maintainer['handle'],
  29236. $maintainer['name'], $maintainer['email']);
  29237. }
  29238. $pf->setVersion($pf2->getVersion());
  29239. $pf->setDate($pf2->getDate());
  29240. $pf->setLicense($pf2->getLicense());
  29241. $pf->setState($pf2->getState());
  29242. $pf->setNotes($pf2->getNotes());
  29243. $pf->addPhpDep($deps['required']['php']['min'], 'ge');
  29244. if (isset($deps['required']['php']['max'])) {
  29245. $pf->addPhpDep($deps['required']['php']['max'], 'le');
  29246. }
  29247. if (isset($deps['required']['package'])) {
  29248. if (!isset($deps['required']['package'][0])) {
  29249. $deps['required']['package'] = array($deps['required']['package']);
  29250. }
  29251. foreach ($deps['required']['package'] as $dep) {
  29252. if (!isset($dep['channel'])) {
  29253. return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
  29254. ' contains uri-based dependency on a package. Using a ' .
  29255. 'PEAR_PackageFileManager-based script is an option');
  29256. }
  29257. if ($dep['channel'] != 'pear.php.net'
  29258. && $dep['channel'] != 'pecl.php.net'
  29259. && $dep['channel'] != 'doc.php.net') {
  29260. return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
  29261. ' contains dependency on a non-standard channel package. Using a ' .
  29262. 'PEAR_PackageFileManager-based script is an option');
  29263. }
  29264. if (isset($dep['conflicts'])) {
  29265. return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
  29266. ' contains conflicts dependency. Using a ' .
  29267. 'PEAR_PackageFileManager-based script is an option');
  29268. }
  29269. if (isset($dep['exclude'])) {
  29270. $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
  29271. }
  29272. if (isset($dep['min'])) {
  29273. $pf->addPackageDep($dep['name'], $dep['min'], 'ge');
  29274. }
  29275. if (isset($dep['max'])) {
  29276. $pf->addPackageDep($dep['name'], $dep['max'], 'le');
  29277. }
  29278. }
  29279. }
  29280. if (isset($deps['required']['extension'])) {
  29281. if (!isset($deps['required']['extension'][0])) {
  29282. $deps['required']['extension'] = array($deps['required']['extension']);
  29283. }
  29284. foreach ($deps['required']['extension'] as $dep) {
  29285. if (isset($dep['conflicts'])) {
  29286. return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
  29287. ' contains conflicts dependency. Using a ' .
  29288. 'PEAR_PackageFileManager-based script is an option');
  29289. }
  29290. if (isset($dep['exclude'])) {
  29291. $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
  29292. }
  29293. if (isset($dep['min'])) {
  29294. $pf->addExtensionDep($dep['name'], $dep['min'], 'ge');
  29295. }
  29296. if (isset($dep['max'])) {
  29297. $pf->addExtensionDep($dep['name'], $dep['max'], 'le');
  29298. }
  29299. }
  29300. }
  29301. if (isset($deps['optional']['package'])) {
  29302. if (!isset($deps['optional']['package'][0])) {
  29303. $deps['optional']['package'] = array($deps['optional']['package']);
  29304. }
  29305. foreach ($deps['optional']['package'] as $dep) {
  29306. if (!isset($dep['channel'])) {
  29307. return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
  29308. ' contains uri-based dependency on a package. Using a ' .
  29309. 'PEAR_PackageFileManager-based script is an option');
  29310. }
  29311. if ($dep['channel'] != 'pear.php.net'
  29312. && $dep['channel'] != 'pecl.php.net'
  29313. && $dep['channel'] != 'doc.php.net') {
  29314. return $this->raiseError('Cannot safely convert "' . $packagexml . '"' .
  29315. ' contains dependency on a non-standard channel package. Using a ' .
  29316. 'PEAR_PackageFileManager-based script is an option');
  29317. }
  29318. if (isset($dep['exclude'])) {
  29319. $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
  29320. }
  29321. if (isset($dep['min'])) {
  29322. $pf->addPackageDep($dep['name'], $dep['min'], 'ge', 'yes');
  29323. }
  29324. if (isset($dep['max'])) {
  29325. $pf->addPackageDep($dep['name'], $dep['max'], 'le', 'yes');
  29326. }
  29327. }
  29328. }
  29329. if (isset($deps['optional']['extension'])) {
  29330. if (!isset($deps['optional']['extension'][0])) {
  29331. $deps['optional']['extension'] = array($deps['optional']['extension']);
  29332. }
  29333. foreach ($deps['optional']['extension'] as $dep) {
  29334. if (isset($dep['exclude'])) {
  29335. $this->ui->outputData('WARNING: exclude tags are ignored in conversion');
  29336. }
  29337. if (isset($dep['min'])) {
  29338. $pf->addExtensionDep($dep['name'], $dep['min'], 'ge', 'yes');
  29339. }
  29340. if (isset($dep['max'])) {
  29341. $pf->addExtensionDep($dep['name'], $dep['max'], 'le', 'yes');
  29342. }
  29343. }
  29344. }
  29345. $contents = $pf2->getContents();
  29346. $release = $pf2->getReleases();
  29347. if (isset($releases[0])) {
  29348. return $this->raiseError('Cannot safely process "' . $packagexml . '" contains '
  29349. . 'multiple extsrcrelease/zendextsrcrelease tags. Using a PEAR_PackageFileManager-based script ' .
  29350. 'or the convert command is an option');
  29351. }
  29352. if ($configoptions = $pf2->getConfigureOptions()) {
  29353. foreach ($configoptions as $option) {
  29354. $default = isset($option['default']) ? $option['default'] : false;
  29355. $pf->addConfigureOption($option['name'], $option['prompt'], $default);
  29356. }
  29357. }
  29358. if (isset($release['filelist']['ignore'])) {
  29359. return $this->raiseError('Cannot safely process "' . $packagexml . '" contains '
  29360. . 'ignore tags. Using a PEAR_PackageFileManager-based script or the convert' .
  29361. ' command is an option');
  29362. }
  29363. if (isset($release['filelist']['install']) &&
  29364. !isset($release['filelist']['install'][0])) {
  29365. $release['filelist']['install'] = array($release['filelist']['install']);
  29366. }
  29367. if (isset($contents['dir']['attribs']['baseinstalldir'])) {
  29368. $baseinstalldir = $contents['dir']['attribs']['baseinstalldir'];
  29369. } else {
  29370. $baseinstalldir = false;
  29371. }
  29372. if (!isset($contents['dir']['file'][0])) {
  29373. $contents['dir']['file'] = array($contents['dir']['file']);
  29374. }
  29375. foreach ($contents['dir']['file'] as $file) {
  29376. if ($baseinstalldir && !isset($file['attribs']['baseinstalldir'])) {
  29377. $file['attribs']['baseinstalldir'] = $baseinstalldir;
  29378. }
  29379. $processFile = $file;
  29380. unset($processFile['attribs']);
  29381. if (count($processFile)) {
  29382. foreach ($processFile as $name => $task) {
  29383. if ($name != $pf2->getTasksNs() . ':replace') {
  29384. return $this->raiseError('Cannot safely process "' . $packagexml .
  29385. '" contains tasks other than replace. Using a ' .
  29386. 'PEAR_PackageFileManager-based script is an option.');
  29387. }
  29388. $file['attribs']['replace'][] = $task;
  29389. }
  29390. }
  29391. if (!in_array($file['attribs']['role'], PEAR_Common::getFileRoles())) {
  29392. return $this->raiseError('Cannot safely convert "' . $packagexml .
  29393. '", contains custom roles. Using a PEAR_PackageFileManager-based script ' .
  29394. 'or the convert command is an option');
  29395. }
  29396. if (isset($release['filelist']['install'])) {
  29397. foreach ($release['filelist']['install'] as $installas) {
  29398. if ($installas['attribs']['name'] == $file['attribs']['name']) {
  29399. $file['attribs']['install-as'] = $installas['attribs']['as'];
  29400. }
  29401. }
  29402. }
  29403. $pf->addFile('/', $file['attribs']['name'], $file['attribs']);
  29404. }
  29405. if ($pf2->getChangeLog()) {
  29406. $this->ui->outputData('WARNING: changelog is not translated to package.xml ' .
  29407. '1.0, use PEAR_PackageFileManager-based script if you need changelog-' .
  29408. 'translation for package.xml 1.0');
  29409. }
  29410. $gen = &$pf->getDefaultGenerator();
  29411. $gen->toPackageFile('.');
  29412. }
  29413. }
  29414. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Registry.xml��������������������������������������������������������������0000644�0001750�0001750�00000003376�14720722517�016736� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<commands version="1.0">
  29415. <list>
  29416. <summary>List Installed Packages In The Default Channel</summary>
  29417. <function>doList</function>
  29418. <shortcut>l</shortcut>
  29419. <options>
  29420. <channel>
  29421. <shortopt>c</shortopt>
  29422. <doc>list installed packages from this channel</doc>
  29423. <arg>CHAN</arg>
  29424. </channel>
  29425. <allchannels>
  29426. <shortopt>a</shortopt>
  29427. <doc>list installed packages from all channels</doc>
  29428. </allchannels>
  29429. <channelinfo>
  29430. <shortopt>i</shortopt>
  29431. <doc>output fully channel-aware data, even on failure</doc>
  29432. </channelinfo>
  29433. </options>
  29434. <doc>&lt;package&gt;
  29435. If invoked without parameters, this command lists the PEAR packages
  29436. installed in your php_dir ({config php_dir}). With a parameter, it
  29437. lists the files in a package.
  29438. </doc>
  29439. </list>
  29440. <list-files>
  29441. <summary>List Files In Installed Package</summary>
  29442. <function>doFileList</function>
  29443. <shortcut>fl</shortcut>
  29444. <options />
  29445. <doc>&lt;package&gt;
  29446. List the files in an installed package.
  29447. </doc>
  29448. </list-files>
  29449. <shell-test>
  29450. <summary>Shell Script Test</summary>
  29451. <function>doShellTest</function>
  29452. <shortcut>st</shortcut>
  29453. <options />
  29454. <doc>&lt;package&gt; [[relation] version]
  29455. Tests if a package is installed in the system. Will exit(1) if it is not.
  29456. &lt;relation&gt; The version comparison operator. One of:
  29457. &lt;, lt, &lt;=, le, &gt;, gt, &gt;=, ge, ==, =, eq, !=, &lt;&gt;, ne
  29458. &lt;version&gt; The version to compare with
  29459. </doc>
  29460. </shell-test>
  29461. <info>
  29462. <summary>Display information about a package</summary>
  29463. <function>doInfo</function>
  29464. <shortcut>in</shortcut>
  29465. <options />
  29466. <doc>&lt;package&gt;
  29467. Displays information about a package. The package argument may be a
  29468. local package file, an URL to a package file, or the name of an
  29469. installed package.</doc>
  29470. </info>
  29471. </commands>������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Registry.php��������������������������������������������������������������0000664�0001750�0001750�00000132315�14720722517�016723� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  29472. /**
  29473. * PEAR_Command_Registry (list, list-files, shell-test, info commands)
  29474. *
  29475. * PHP versions 4 and 5
  29476. *
  29477. * @category pear
  29478. * @package PEAR
  29479. * @author Stig Bakken <ssb@php.net>
  29480. * @author Greg Beaver <cellog@php.net>
  29481. * @copyright 1997-2009 The Authors
  29482. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  29483. * @link http://pear.php.net/package/PEAR
  29484. * @since File available since Release 0.1
  29485. */
  29486. /**
  29487. * base class
  29488. */
  29489. require_once 'PEAR/Command/Common.php';
  29490. /**
  29491. * PEAR commands for registry manipulation
  29492. *
  29493. * @category pear
  29494. * @package PEAR
  29495. * @author Stig Bakken <ssb@php.net>
  29496. * @author Greg Beaver <cellog@php.net>
  29497. * @copyright 1997-2009 The Authors
  29498. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  29499. * @version Release: 1.10.16
  29500. * @link http://pear.php.net/package/PEAR
  29501. * @since Class available since Release 0.1
  29502. */
  29503. class PEAR_Command_Registry extends PEAR_Command_Common
  29504. {
  29505. var $commands = array(
  29506. 'list' => array(
  29507. 'summary' => 'List Installed Packages In The Default Channel',
  29508. 'function' => 'doList',
  29509. 'shortcut' => 'l',
  29510. 'options' => array(
  29511. 'channel' => array(
  29512. 'shortopt' => 'c',
  29513. 'doc' => 'list installed packages from this channel',
  29514. 'arg' => 'CHAN',
  29515. ),
  29516. 'allchannels' => array(
  29517. 'shortopt' => 'a',
  29518. 'doc' => 'list installed packages from all channels',
  29519. ),
  29520. 'channelinfo' => array(
  29521. 'shortopt' => 'i',
  29522. 'doc' => 'output fully channel-aware data, even on failure',
  29523. ),
  29524. ),
  29525. 'doc' => '<package>
  29526. If invoked without parameters, this command lists the PEAR packages
  29527. installed in your php_dir ({config php_dir}). With a parameter, it
  29528. lists the files in a package.
  29529. ',
  29530. ),
  29531. 'list-files' => array(
  29532. 'summary' => 'List Files In Installed Package',
  29533. 'function' => 'doFileList',
  29534. 'shortcut' => 'fl',
  29535. 'options' => array(),
  29536. 'doc' => '<package>
  29537. List the files in an installed package.
  29538. '
  29539. ),
  29540. 'shell-test' => array(
  29541. 'summary' => 'Shell Script Test',
  29542. 'function' => 'doShellTest',
  29543. 'shortcut' => 'st',
  29544. 'options' => array(),
  29545. 'doc' => '<package> [[relation] version]
  29546. Tests if a package is installed in the system. Will exit(1) if it is not.
  29547. <relation> The version comparison operator. One of:
  29548. <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne
  29549. <version> The version to compare with
  29550. '),
  29551. 'info' => array(
  29552. 'summary' => 'Display information about a package',
  29553. 'function' => 'doInfo',
  29554. 'shortcut' => 'in',
  29555. 'options' => array(),
  29556. 'doc' => '<package>
  29557. Displays information about a package. The package argument may be a
  29558. local package file, an URL to a package file, or the name of an
  29559. installed package.'
  29560. )
  29561. );
  29562. /**
  29563. * PEAR_Command_Registry constructor.
  29564. *
  29565. * @access public
  29566. */
  29567. function __construct(&$ui, &$config)
  29568. {
  29569. parent::__construct($ui, $config);
  29570. }
  29571. function _sortinfo($a, $b)
  29572. {
  29573. $apackage = isset($a['package']) ? $a['package'] : $a['name'];
  29574. $bpackage = isset($b['package']) ? $b['package'] : $b['name'];
  29575. return strcmp($apackage, $bpackage);
  29576. }
  29577. function doList($command, $options, $params)
  29578. {
  29579. $reg = &$this->config->getRegistry();
  29580. $channelinfo = isset($options['channelinfo']);
  29581. if (isset($options['allchannels']) && !$channelinfo) {
  29582. return $this->doListAll($command, array(), $params);
  29583. }
  29584. if (isset($options['allchannels']) && $channelinfo) {
  29585. // allchannels with $channelinfo
  29586. unset($options['allchannels']);
  29587. $channels = $reg->getChannels();
  29588. $errors = array();
  29589. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  29590. foreach ($channels as $channel) {
  29591. $options['channel'] = $channel->getName();
  29592. $ret = $this->doList($command, $options, $params);
  29593. if (PEAR::isError($ret)) {
  29594. $errors[] = $ret;
  29595. }
  29596. }
  29597. PEAR::staticPopErrorHandling();
  29598. if (count($errors)) {
  29599. // for now, only give first error
  29600. return PEAR::raiseError($errors[0]);
  29601. }
  29602. return true;
  29603. }
  29604. if (count($params) === 1) {
  29605. return $this->doFileList($command, $options, $params);
  29606. }
  29607. if (isset($options['channel'])) {
  29608. if (!$reg->channelExists($options['channel'])) {
  29609. return $this->raiseError('Channel "' . $options['channel'] .'" does not exist');
  29610. }
  29611. $channel = $reg->channelName($options['channel']);
  29612. } else {
  29613. $channel = $this->config->get('default_channel');
  29614. }
  29615. $installed = $reg->packageInfo(null, null, $channel);
  29616. usort($installed, array(&$this, '_sortinfo'));
  29617. $data = array(
  29618. 'caption' => 'Installed packages, channel ' .
  29619. $channel . ':',
  29620. 'border' => true,
  29621. 'headline' => array('Package', 'Version', 'State'),
  29622. 'channel' => $channel,
  29623. );
  29624. if ($channelinfo) {
  29625. $data['headline'] = array('Channel', 'Package', 'Version', 'State');
  29626. }
  29627. if (count($installed) && !isset($data['data'])) {
  29628. $data['data'] = array();
  29629. }
  29630. foreach ($installed as $package) {
  29631. $pobj = $reg->getPackage(isset($package['package']) ?
  29632. $package['package'] : $package['name'], $channel);
  29633. if ($channelinfo) {
  29634. $packageinfo = array($pobj->getChannel(), $pobj->getPackage(), $pobj->getVersion(),
  29635. $pobj->getState() ? $pobj->getState() : null);
  29636. } else {
  29637. $packageinfo = array($pobj->getPackage(), $pobj->getVersion(),
  29638. $pobj->getState() ? $pobj->getState() : null);
  29639. }
  29640. $data['data'][] = $packageinfo;
  29641. }
  29642. if (count($installed) === 0) {
  29643. if (!$channelinfo) {
  29644. $data = '(no packages installed from channel ' . $channel . ')';
  29645. } else {
  29646. $data = array(
  29647. 'caption' => 'Installed packages, channel ' .
  29648. $channel . ':',
  29649. 'border' => true,
  29650. 'channel' => $channel,
  29651. 'data' => array(array('(no packages installed)')),
  29652. );
  29653. }
  29654. }
  29655. $this->ui->outputData($data, $command);
  29656. return true;
  29657. }
  29658. function doListAll($command, $options, $params)
  29659. {
  29660. // This duplicate code is deprecated over
  29661. // list --channelinfo, which gives identical
  29662. // output for list and list --allchannels.
  29663. $reg = &$this->config->getRegistry();
  29664. $installed = $reg->packageInfo(null, null, null);
  29665. foreach ($installed as $channel => $packages) {
  29666. usort($packages, array($this, '_sortinfo'));
  29667. $data = array(
  29668. 'caption' => 'Installed packages, channel ' . $channel . ':',
  29669. 'border' => true,
  29670. 'headline' => array('Package', 'Version', 'State'),
  29671. 'channel' => $channel
  29672. );
  29673. foreach ($packages as $package) {
  29674. $p = isset($package['package']) ? $package['package'] : $package['name'];
  29675. $pobj = $reg->getPackage($p, $channel);
  29676. $data['data'][] = array($pobj->getPackage(), $pobj->getVersion(),
  29677. $pobj->getState() ? $pobj->getState() : null);
  29678. }
  29679. // Adds a blank line after each section
  29680. $data['data'][] = array();
  29681. if (count($packages) === 0) {
  29682. $data = array(
  29683. 'caption' => 'Installed packages, channel ' . $channel . ':',
  29684. 'border' => true,
  29685. 'data' => array(array('(no packages installed)'), array()),
  29686. 'channel' => $channel
  29687. );
  29688. }
  29689. $this->ui->outputData($data, $command);
  29690. }
  29691. return true;
  29692. }
  29693. function doFileList($command, $options, $params)
  29694. {
  29695. if (count($params) !== 1) {
  29696. return $this->raiseError('list-files expects 1 parameter');
  29697. }
  29698. $reg = &$this->config->getRegistry();
  29699. $fp = false;
  29700. if (!is_dir($params[0]) && (file_exists($params[0]) || $fp = @fopen($params[0], 'r'))) {
  29701. if ($fp) {
  29702. fclose($fp);
  29703. }
  29704. if (!class_exists('PEAR_PackageFile')) {
  29705. require_once 'PEAR/PackageFile.php';
  29706. }
  29707. $pkg = new PEAR_PackageFile($this->config, $this->_debug);
  29708. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  29709. $info = &$pkg->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL);
  29710. PEAR::staticPopErrorHandling();
  29711. $headings = array('Package File', 'Install Path');
  29712. $installed = false;
  29713. } else {
  29714. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  29715. $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
  29716. PEAR::staticPopErrorHandling();
  29717. if (PEAR::isError($parsed)) {
  29718. return $this->raiseError($parsed);
  29719. }
  29720. $info = &$reg->getPackage($parsed['package'], $parsed['channel']);
  29721. $headings = array('Type', 'Install Path');
  29722. $installed = true;
  29723. }
  29724. if (PEAR::isError($info)) {
  29725. return $this->raiseError($info);
  29726. }
  29727. if ($info === null) {
  29728. return $this->raiseError("`$params[0]' not installed");
  29729. }
  29730. $list = ($info->getPackagexmlVersion() == '1.0' || $installed) ?
  29731. $info->getFilelist() : $info->getContents();
  29732. if ($installed) {
  29733. $caption = 'Installed Files For ' . $params[0];
  29734. } else {
  29735. $caption = 'Contents of ' . basename($params[0]);
  29736. }
  29737. $data = array(
  29738. 'caption' => $caption,
  29739. 'border' => true,
  29740. 'headline' => $headings);
  29741. if ($info->getPackagexmlVersion() == '1.0' || $installed) {
  29742. foreach ($list as $file => $att) {
  29743. if ($installed) {
  29744. if (empty($att['installed_as'])) {
  29745. continue;
  29746. }
  29747. $data['data'][] = array($att['role'], $att['installed_as']);
  29748. } else {
  29749. if (isset($att['baseinstalldir']) && !in_array($att['role'],
  29750. array('test', 'data', 'doc'))) {
  29751. $dest = $att['baseinstalldir'] . DIRECTORY_SEPARATOR .
  29752. $file;
  29753. } else {
  29754. $dest = $file;
  29755. }
  29756. switch ($att['role']) {
  29757. case 'test':
  29758. case 'data':
  29759. case 'doc':
  29760. $role = $att['role'];
  29761. if ($role == 'test') {
  29762. $role .= 's';
  29763. }
  29764. $dest = $this->config->get($role . '_dir') . DIRECTORY_SEPARATOR .
  29765. $info->getPackage() . DIRECTORY_SEPARATOR . $dest;
  29766. break;
  29767. case 'php':
  29768. default:
  29769. $dest = $this->config->get('php_dir') . DIRECTORY_SEPARATOR .
  29770. $dest;
  29771. }
  29772. $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
  29773. $dest = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
  29774. array(DIRECTORY_SEPARATOR,
  29775. DIRECTORY_SEPARATOR,
  29776. DIRECTORY_SEPARATOR),
  29777. $dest);
  29778. $file = preg_replace('!/+!', '/', $file);
  29779. $data['data'][] = array($file, $dest);
  29780. }
  29781. }
  29782. } else { // package.xml 2.0, not installed
  29783. if (!isset($list['dir']['file'][0])) {
  29784. $list['dir']['file'] = array($list['dir']['file']);
  29785. }
  29786. foreach ($list['dir']['file'] as $att) {
  29787. $att = $att['attribs'];
  29788. $file = $att['name'];
  29789. $role = &PEAR_Installer_Role::factory($info, $att['role'], $this->config);
  29790. $role->setup($this, $info, $att, $file);
  29791. if (!$role->isInstallable()) {
  29792. $dest = '(not installable)';
  29793. } else {
  29794. $dest = $role->processInstallation($info, $att, $file, '');
  29795. if (PEAR::isError($dest)) {
  29796. $dest = '(Unknown role "' . $att['role'] . ')';
  29797. } else {
  29798. list(,, $dest) = $dest;
  29799. }
  29800. }
  29801. $data['data'][] = array($file, $dest);
  29802. }
  29803. }
  29804. $this->ui->outputData($data, $command);
  29805. return true;
  29806. }
  29807. function doShellTest($command, $options, $params)
  29808. {
  29809. if (count($params) < 1) {
  29810. return PEAR::raiseError('ERROR, usage: pear shell-test packagename [[relation] version]');
  29811. }
  29812. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  29813. $reg = &$this->config->getRegistry();
  29814. $info = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
  29815. if (PEAR::isError($info)) {
  29816. exit(1); // invalid package name
  29817. }
  29818. $package = $info['package'];
  29819. $channel = $info['channel'];
  29820. // "pear shell-test Foo"
  29821. if (!$reg->packageExists($package, $channel)) {
  29822. if ($channel == 'pecl.php.net') {
  29823. if ($reg->packageExists($package, 'pear.php.net')) {
  29824. $channel = 'pear.php.net'; // magically change channels for extensions
  29825. }
  29826. }
  29827. }
  29828. if (count($params) === 1) {
  29829. if (!$reg->packageExists($package, $channel)) {
  29830. exit(1);
  29831. }
  29832. // "pear shell-test Foo 1.0"
  29833. } elseif (count($params) === 2) {
  29834. $v = $reg->packageInfo($package, 'version', $channel);
  29835. if (!$v || !version_compare("$v", "{$params[1]}", "ge")) {
  29836. exit(1);
  29837. }
  29838. // "pear shell-test Foo ge 1.0"
  29839. } elseif (count($params) === 3) {
  29840. $v = $reg->packageInfo($package, 'version', $channel);
  29841. if (!$v || !version_compare("$v", "{$params[2]}", $params[1])) {
  29842. exit(1);
  29843. }
  29844. } else {
  29845. PEAR::staticPopErrorHandling();
  29846. $this->raiseError("$command: expects 1 to 3 parameters");
  29847. exit(1);
  29848. }
  29849. }
  29850. function doInfo($command, $options, $params)
  29851. {
  29852. if (count($params) !== 1) {
  29853. return $this->raiseError('pear info expects 1 parameter');
  29854. }
  29855. $info = $fp = false;
  29856. $reg = &$this->config->getRegistry();
  29857. if (is_file($params[0]) && !is_dir($params[0]) &&
  29858. (file_exists($params[0]) || $fp = @fopen($params[0], 'r'))
  29859. ) {
  29860. if ($fp) {
  29861. fclose($fp);
  29862. }
  29863. if (!class_exists('PEAR_PackageFile')) {
  29864. require_once 'PEAR/PackageFile.php';
  29865. }
  29866. $pkg = new PEAR_PackageFile($this->config, $this->_debug);
  29867. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  29868. $obj = &$pkg->fromAnyFile($params[0], PEAR_VALIDATE_NORMAL);
  29869. PEAR::staticPopErrorHandling();
  29870. if (PEAR::isError($obj)) {
  29871. $uinfo = $obj->getUserInfo();
  29872. if (is_array($uinfo)) {
  29873. foreach ($uinfo as $message) {
  29874. if (is_array($message)) {
  29875. $message = $message['message'];
  29876. }
  29877. $this->ui->outputData($message);
  29878. }
  29879. }
  29880. return $this->raiseError($obj);
  29881. }
  29882. if ($obj->getPackagexmlVersion() != '1.0') {
  29883. return $this->_doInfo2($command, $options, $params, $obj, false);
  29884. }
  29885. $info = $obj->toArray();
  29886. } else {
  29887. $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
  29888. if (PEAR::isError($parsed)) {
  29889. return $this->raiseError($parsed);
  29890. }
  29891. $package = $parsed['package'];
  29892. $channel = $parsed['channel'];
  29893. $info = $reg->packageInfo($package, null, $channel);
  29894. if (isset($info['old'])) {
  29895. $obj = $reg->getPackage($package, $channel);
  29896. return $this->_doInfo2($command, $options, $params, $obj, true);
  29897. }
  29898. }
  29899. if (PEAR::isError($info)) {
  29900. return $info;
  29901. }
  29902. if (empty($info)) {
  29903. $this->raiseError("No information found for `$params[0]'");
  29904. return;
  29905. }
  29906. unset($info['filelist']);
  29907. unset($info['dirtree']);
  29908. unset($info['changelog']);
  29909. if (isset($info['xsdversion'])) {
  29910. $info['package.xml version'] = $info['xsdversion'];
  29911. unset($info['xsdversion']);
  29912. }
  29913. if (isset($info['packagerversion'])) {
  29914. $info['packaged with PEAR version'] = $info['packagerversion'];
  29915. unset($info['packagerversion']);
  29916. }
  29917. $keys = array_keys($info);
  29918. $longtext = array('description', 'summary');
  29919. foreach ($keys as $key) {
  29920. if (is_array($info[$key])) {
  29921. switch ($key) {
  29922. case 'maintainers': {
  29923. $i = 0;
  29924. $mstr = '';
  29925. foreach ($info[$key] as $m) {
  29926. if ($i++ > 0) {
  29927. $mstr .= "\n";
  29928. }
  29929. $mstr .= $m['name'] . " <";
  29930. if (isset($m['email'])) {
  29931. $mstr .= $m['email'];
  29932. } else {
  29933. $mstr .= $m['handle'] . '@php.net';
  29934. }
  29935. $mstr .= "> ($m[role])";
  29936. }
  29937. $info[$key] = $mstr;
  29938. break;
  29939. }
  29940. case 'release_deps': {
  29941. $i = 0;
  29942. $dstr = '';
  29943. foreach ($info[$key] as $d) {
  29944. if (isset($this->_deps_rel_trans[$d['rel']])) {
  29945. $rel = $this->_deps_rel_trans[$d['rel']];
  29946. } else {
  29947. $rel = $d['rel'];
  29948. }
  29949. if (isset($this->_deps_type_trans[$d['type']])) {
  29950. $type = ucfirst($this->_deps_type_trans[$d['type']]);
  29951. } else {
  29952. $type = $d['type'];
  29953. }
  29954. if (isset($d['name'])) {
  29955. $name = $d['name'] . ' ';
  29956. } else {
  29957. $name = '';
  29958. }
  29959. if (isset($d['version'])) {
  29960. $version = $d['version'] . ' ';
  29961. } else {
  29962. $version = '';
  29963. }
  29964. if (isset($d['optional']) && $d['optional'] == 'yes') {
  29965. $optional = ' (optional)';
  29966. } else {
  29967. $optional = '';
  29968. }
  29969. $dstr .= "$type $name$rel $version$optional\n";
  29970. }
  29971. $info[$key] = $dstr;
  29972. break;
  29973. }
  29974. case 'provides' : {
  29975. $debug = $this->config->get('verbose');
  29976. if ($debug < 2) {
  29977. $pstr = 'Classes: ';
  29978. } else {
  29979. $pstr = '';
  29980. }
  29981. $i = 0;
  29982. foreach ($info[$key] as $p) {
  29983. if ($debug < 2 && $p['type'] != "class") {
  29984. continue;
  29985. }
  29986. // Only print classes when verbosity mode is < 2
  29987. if ($debug < 2) {
  29988. if ($i++ > 0) {
  29989. $pstr .= ", ";
  29990. }
  29991. $pstr .= $p['name'];
  29992. } else {
  29993. if ($i++ > 0) {
  29994. $pstr .= "\n";
  29995. }
  29996. $pstr .= ucfirst($p['type']) . " " . $p['name'];
  29997. if (isset($p['explicit']) && $p['explicit'] == 1) {
  29998. $pstr .= " (explicit)";
  29999. }
  30000. }
  30001. }
  30002. $info[$key] = $pstr;
  30003. break;
  30004. }
  30005. case 'configure_options' : {
  30006. foreach ($info[$key] as $i => $p) {
  30007. $info[$key][$i] = array_map(null, array_keys($p), array_values($p));
  30008. $info[$key][$i] = array_map(
  30009. function($a) { return join(" = ", $a); },
  30010. $info[$key][$i]);
  30011. $info[$key][$i] = implode(', ', $info[$key][$i]);
  30012. }
  30013. $info[$key] = implode("\n", $info[$key]);
  30014. break;
  30015. }
  30016. default: {
  30017. $info[$key] = implode(", ", $info[$key]);
  30018. break;
  30019. }
  30020. }
  30021. }
  30022. if ($key == '_lastmodified') {
  30023. $hdate = date('Y-m-d', $info[$key]);
  30024. unset($info[$key]);
  30025. $info['Last Modified'] = $hdate;
  30026. } elseif ($key == '_lastversion') {
  30027. $info['Previous Installed Version'] = $info[$key] ? $info[$key] : '- None -';
  30028. unset($info[$key]);
  30029. } else {
  30030. $info[$key] = trim($info[$key]);
  30031. if (in_array($key, $longtext)) {
  30032. $info[$key] = preg_replace('/ +/', ' ', $info[$key]);
  30033. }
  30034. }
  30035. }
  30036. $caption = 'About ' . $info['package'] . '-' . $info['version'];
  30037. $data = array(
  30038. 'caption' => $caption,
  30039. 'border' => true);
  30040. foreach ($info as $key => $value) {
  30041. $key = ucwords(trim(str_replace('_', ' ', $key)));
  30042. $data['data'][] = array($key, $value);
  30043. }
  30044. $data['raw'] = $info;
  30045. $this->ui->outputData($data, 'package-info');
  30046. }
  30047. /**
  30048. * @access private
  30049. */
  30050. function _doInfo2($command, $options, $params, &$obj, $installed)
  30051. {
  30052. $reg = &$this->config->getRegistry();
  30053. $caption = 'About ' . $obj->getChannel() . '/' .$obj->getPackage() . '-' .
  30054. $obj->getVersion();
  30055. $data = array(
  30056. 'caption' => $caption,
  30057. 'border' => true);
  30058. switch ($obj->getPackageType()) {
  30059. case 'php' :
  30060. $release = 'PEAR-style PHP-based Package';
  30061. break;
  30062. case 'extsrc' :
  30063. $release = 'PECL-style PHP extension (source code)';
  30064. break;
  30065. case 'zendextsrc' :
  30066. $release = 'PECL-style Zend extension (source code)';
  30067. break;
  30068. case 'extbin' :
  30069. $release = 'PECL-style PHP extension (binary)';
  30070. break;
  30071. case 'zendextbin' :
  30072. $release = 'PECL-style Zend extension (binary)';
  30073. break;
  30074. case 'bundle' :
  30075. $release = 'Package bundle (collection of packages)';
  30076. break;
  30077. }
  30078. $extends = $obj->getExtends();
  30079. $extends = $extends ?
  30080. $obj->getPackage() . ' (extends ' . $extends . ')' : $obj->getPackage();
  30081. if ($src = $obj->getSourcePackage()) {
  30082. $extends .= ' (source package ' . $src['channel'] . '/' . $src['package'] . ')';
  30083. }
  30084. $info = array(
  30085. 'Release Type' => $release,
  30086. 'Name' => $extends,
  30087. 'Channel' => $obj->getChannel(),
  30088. 'Summary' => preg_replace('/ +/', ' ', $obj->getSummary()),
  30089. 'Description' => preg_replace('/ +/', ' ', $obj->getDescription()),
  30090. );
  30091. $info['Maintainers'] = '';
  30092. foreach (array('lead', 'developer', 'contributor', 'helper') as $role) {
  30093. $leads = $obj->{"get{$role}s"}();
  30094. if (!$leads) {
  30095. continue;
  30096. }
  30097. if (isset($leads['active'])) {
  30098. $leads = array($leads);
  30099. }
  30100. foreach ($leads as $lead) {
  30101. if (!empty($info['Maintainers'])) {
  30102. $info['Maintainers'] .= "\n";
  30103. }
  30104. $active = $lead['active'] == 'no' ? ', inactive' : '';
  30105. $info['Maintainers'] .= $lead['name'] . ' <';
  30106. $info['Maintainers'] .= $lead['email'] . "> ($role$active)";
  30107. }
  30108. }
  30109. $info['Release Date'] = $obj->getDate();
  30110. if ($time = $obj->getTime()) {
  30111. $info['Release Date'] .= ' ' . $time;
  30112. }
  30113. $info['Release Version'] = $obj->getVersion() . ' (' . $obj->getState() . ')';
  30114. $info['API Version'] = $obj->getVersion('api') . ' (' . $obj->getState('api') . ')';
  30115. $info['License'] = $obj->getLicense();
  30116. $uri = $obj->getLicenseLocation();
  30117. if ($uri) {
  30118. if (isset($uri['uri'])) {
  30119. $info['License'] .= ' (' . $uri['uri'] . ')';
  30120. } else {
  30121. $extra = $obj->getInstalledLocation($info['filesource']);
  30122. if ($extra) {
  30123. $info['License'] .= ' (' . $uri['filesource'] . ')';
  30124. }
  30125. }
  30126. }
  30127. $info['Release Notes'] = $obj->getNotes();
  30128. if ($compat = $obj->getCompatible()) {
  30129. if (!isset($compat[0])) {
  30130. $compat = array($compat);
  30131. }
  30132. $info['Compatible with'] = '';
  30133. foreach ($compat as $package) {
  30134. $info['Compatible with'] .= $package['channel'] . '/' . $package['name'] .
  30135. "\nVersions >= " . $package['min'] . ', <= ' . $package['max'];
  30136. if (isset($package['exclude'])) {
  30137. if (is_array($package['exclude'])) {
  30138. $package['exclude'] = implode(', ', $package['exclude']);
  30139. }
  30140. if (!isset($info['Not Compatible with'])) {
  30141. $info['Not Compatible with'] = '';
  30142. } else {
  30143. $info['Not Compatible with'] .= "\n";
  30144. }
  30145. $info['Not Compatible with'] .= $package['channel'] . '/' .
  30146. $package['name'] . "\nVersions " . $package['exclude'];
  30147. }
  30148. }
  30149. }
  30150. $usesrole = $obj->getUsesrole();
  30151. if ($usesrole) {
  30152. if (!isset($usesrole[0])) {
  30153. $usesrole = array($usesrole);
  30154. }
  30155. foreach ($usesrole as $roledata) {
  30156. if (isset($info['Uses Custom Roles'])) {
  30157. $info['Uses Custom Roles'] .= "\n";
  30158. } else {
  30159. $info['Uses Custom Roles'] = '';
  30160. }
  30161. if (isset($roledata['package'])) {
  30162. $rolepackage = $reg->parsedPackageNameToString($roledata, true);
  30163. } else {
  30164. $rolepackage = $roledata['uri'];
  30165. }
  30166. $info['Uses Custom Roles'] .= $roledata['role'] . ' (' . $rolepackage . ')';
  30167. }
  30168. }
  30169. $usestask = $obj->getUsestask();
  30170. if ($usestask) {
  30171. if (!isset($usestask[0])) {
  30172. $usestask = array($usestask);
  30173. }
  30174. foreach ($usestask as $taskdata) {
  30175. if (isset($info['Uses Custom Tasks'])) {
  30176. $info['Uses Custom Tasks'] .= "\n";
  30177. } else {
  30178. $info['Uses Custom Tasks'] = '';
  30179. }
  30180. if (isset($taskdata['package'])) {
  30181. $taskpackage = $reg->parsedPackageNameToString($taskdata, true);
  30182. } else {
  30183. $taskpackage = $taskdata['uri'];
  30184. }
  30185. $info['Uses Custom Tasks'] .= $taskdata['task'] . ' (' . $taskpackage . ')';
  30186. }
  30187. }
  30188. $deps = $obj->getDependencies();
  30189. $info['Required Dependencies'] = 'PHP version ' . $deps['required']['php']['min'];
  30190. if (isset($deps['required']['php']['max'])) {
  30191. $info['Required Dependencies'] .= '-' . $deps['required']['php']['max'] . "\n";
  30192. } else {
  30193. $info['Required Dependencies'] .= "\n";
  30194. }
  30195. if (isset($deps['required']['php']['exclude'])) {
  30196. if (!isset($info['Not Compatible with'])) {
  30197. $info['Not Compatible with'] = '';
  30198. } else {
  30199. $info['Not Compatible with'] .= "\n";
  30200. }
  30201. if (is_array($deps['required']['php']['exclude'])) {
  30202. $deps['required']['php']['exclude'] =
  30203. implode(', ', $deps['required']['php']['exclude']);
  30204. }
  30205. $info['Not Compatible with'] .= "PHP versions\n " .
  30206. $deps['required']['php']['exclude'];
  30207. }
  30208. $info['Required Dependencies'] .= 'PEAR installer version';
  30209. if (isset($deps['required']['pearinstaller']['max'])) {
  30210. $info['Required Dependencies'] .= 's ' .
  30211. $deps['required']['pearinstaller']['min'] . '-' .
  30212. $deps['required']['pearinstaller']['max'];
  30213. } else {
  30214. $info['Required Dependencies'] .= ' ' .
  30215. $deps['required']['pearinstaller']['min'] . ' or newer';
  30216. }
  30217. if (isset($deps['required']['pearinstaller']['exclude'])) {
  30218. if (!isset($info['Not Compatible with'])) {
  30219. $info['Not Compatible with'] = '';
  30220. } else {
  30221. $info['Not Compatible with'] .= "\n";
  30222. }
  30223. if (is_array($deps['required']['pearinstaller']['exclude'])) {
  30224. $deps['required']['pearinstaller']['exclude'] =
  30225. implode(', ', $deps['required']['pearinstaller']['exclude']);
  30226. }
  30227. $info['Not Compatible with'] .= "PEAR installer\n Versions " .
  30228. $deps['required']['pearinstaller']['exclude'];
  30229. }
  30230. foreach (array('Package', 'Extension') as $type) {
  30231. $index = strtolower($type);
  30232. if (isset($deps['required'][$index])) {
  30233. if (isset($deps['required'][$index]['name'])) {
  30234. $deps['required'][$index] = array($deps['required'][$index]);
  30235. }
  30236. foreach ($deps['required'][$index] as $package) {
  30237. if (isset($package['conflicts'])) {
  30238. $infoindex = 'Not Compatible with';
  30239. if (!isset($info['Not Compatible with'])) {
  30240. $info['Not Compatible with'] = '';
  30241. } else {
  30242. $info['Not Compatible with'] .= "\n";
  30243. }
  30244. } else {
  30245. $infoindex = 'Required Dependencies';
  30246. $info[$infoindex] .= "\n";
  30247. }
  30248. if ($index == 'extension') {
  30249. $name = $package['name'];
  30250. } else {
  30251. if (isset($package['channel'])) {
  30252. $name = $package['channel'] . '/' . $package['name'];
  30253. } else {
  30254. $name = '__uri/' . $package['name'] . ' (static URI)';
  30255. }
  30256. }
  30257. $info[$infoindex] .= "$type $name";
  30258. if (isset($package['uri'])) {
  30259. $info[$infoindex] .= "\n Download URI: $package[uri]";
  30260. continue;
  30261. }
  30262. if (isset($package['max']) && isset($package['min'])) {
  30263. $info[$infoindex] .= " \n Versions " .
  30264. $package['min'] . '-' . $package['max'];
  30265. } elseif (isset($package['min'])) {
  30266. $info[$infoindex] .= " \n Version " .
  30267. $package['min'] . ' or newer';
  30268. } elseif (isset($package['max'])) {
  30269. $info[$infoindex] .= " \n Version " .
  30270. $package['max'] . ' or older';
  30271. }
  30272. if (isset($package['recommended'])) {
  30273. $info[$infoindex] .= "\n Recommended version: $package[recommended]";
  30274. }
  30275. if (isset($package['exclude'])) {
  30276. if (!isset($info['Not Compatible with'])) {
  30277. $info['Not Compatible with'] = '';
  30278. } else {
  30279. $info['Not Compatible with'] .= "\n";
  30280. }
  30281. if (is_array($package['exclude'])) {
  30282. $package['exclude'] = implode(', ', $package['exclude']);
  30283. }
  30284. $package['package'] = $package['name']; // for parsedPackageNameToString
  30285. if (isset($package['conflicts'])) {
  30286. $info['Not Compatible with'] .= '=> except ';
  30287. }
  30288. $info['Not Compatible with'] .= 'Package ' .
  30289. $reg->parsedPackageNameToString($package, true);
  30290. $info['Not Compatible with'] .= "\n Versions " . $package['exclude'];
  30291. }
  30292. }
  30293. }
  30294. }
  30295. if (isset($deps['required']['os'])) {
  30296. if (isset($deps['required']['os']['name'])) {
  30297. $dep['required']['os']['name'] = array($dep['required']['os']['name']);
  30298. }
  30299. foreach ($dep['required']['os'] as $os) {
  30300. if (isset($os['conflicts']) && $os['conflicts'] == 'yes') {
  30301. if (!isset($info['Not Compatible with'])) {
  30302. $info['Not Compatible with'] = '';
  30303. } else {
  30304. $info['Not Compatible with'] .= "\n";
  30305. }
  30306. $info['Not Compatible with'] .= "$os[name] Operating System";
  30307. } else {
  30308. $info['Required Dependencies'] .= "\n";
  30309. $info['Required Dependencies'] .= "$os[name] Operating System";
  30310. }
  30311. }
  30312. }
  30313. if (isset($deps['required']['arch'])) {
  30314. if (isset($deps['required']['arch']['pattern'])) {
  30315. $dep['required']['arch']['pattern'] = array($dep['required']['os']['pattern']);
  30316. }
  30317. foreach ($dep['required']['arch'] as $os) {
  30318. if (isset($os['conflicts']) && $os['conflicts'] == 'yes') {
  30319. if (!isset($info['Not Compatible with'])) {
  30320. $info['Not Compatible with'] = '';
  30321. } else {
  30322. $info['Not Compatible with'] .= "\n";
  30323. }
  30324. $info['Not Compatible with'] .= "OS/Arch matching pattern '/$os[pattern]/'";
  30325. } else {
  30326. $info['Required Dependencies'] .= "\n";
  30327. $info['Required Dependencies'] .= "OS/Arch matching pattern '/$os[pattern]/'";
  30328. }
  30329. }
  30330. }
  30331. if (isset($deps['optional'])) {
  30332. foreach (array('Package', 'Extension') as $type) {
  30333. $index = strtolower($type);
  30334. if (isset($deps['optional'][$index])) {
  30335. if (isset($deps['optional'][$index]['name'])) {
  30336. $deps['optional'][$index] = array($deps['optional'][$index]);
  30337. }
  30338. foreach ($deps['optional'][$index] as $package) {
  30339. if (isset($package['conflicts']) && $package['conflicts'] == 'yes') {
  30340. $infoindex = 'Not Compatible with';
  30341. if (!isset($info['Not Compatible with'])) {
  30342. $info['Not Compatible with'] = '';
  30343. } else {
  30344. $info['Not Compatible with'] .= "\n";
  30345. }
  30346. } else {
  30347. $infoindex = 'Optional Dependencies';
  30348. if (!isset($info['Optional Dependencies'])) {
  30349. $info['Optional Dependencies'] = '';
  30350. } else {
  30351. $info['Optional Dependencies'] .= "\n";
  30352. }
  30353. }
  30354. if ($index == 'extension') {
  30355. $name = $package['name'];
  30356. } else {
  30357. if (isset($package['channel'])) {
  30358. $name = $package['channel'] . '/' . $package['name'];
  30359. } else {
  30360. $name = '__uri/' . $package['name'] . ' (static URI)';
  30361. }
  30362. }
  30363. $info[$infoindex] .= "$type $name";
  30364. if (isset($package['uri'])) {
  30365. $info[$infoindex] .= "\n Download URI: $package[uri]";
  30366. continue;
  30367. }
  30368. if ($infoindex == 'Not Compatible with') {
  30369. // conflicts is only used to say that all versions conflict
  30370. continue;
  30371. }
  30372. if (isset($package['max']) && isset($package['min'])) {
  30373. $info[$infoindex] .= " \n Versions " .
  30374. $package['min'] . '-' . $package['max'];
  30375. } elseif (isset($package['min'])) {
  30376. $info[$infoindex] .= " \n Version " .
  30377. $package['min'] . ' or newer';
  30378. } elseif (isset($package['max'])) {
  30379. $info[$infoindex] .= " \n Version " .
  30380. $package['min'] . ' or older';
  30381. }
  30382. if (isset($package['recommended'])) {
  30383. $info[$infoindex] .= "\n Recommended version: $package[recommended]";
  30384. }
  30385. if (isset($package['exclude'])) {
  30386. if (!isset($info['Not Compatible with'])) {
  30387. $info['Not Compatible with'] = '';
  30388. } else {
  30389. $info['Not Compatible with'] .= "\n";
  30390. }
  30391. if (is_array($package['exclude'])) {
  30392. $package['exclude'] = implode(', ', $package['exclude']);
  30393. }
  30394. $info['Not Compatible with'] .= "Package $package\n Versions " .
  30395. $package['exclude'];
  30396. }
  30397. }
  30398. }
  30399. }
  30400. }
  30401. if (isset($deps['group'])) {
  30402. if (!isset($deps['group'][0])) {
  30403. $deps['group'] = array($deps['group']);
  30404. }
  30405. foreach ($deps['group'] as $group) {
  30406. $info['Dependency Group ' . $group['attribs']['name']] = $group['attribs']['hint'];
  30407. $groupindex = $group['attribs']['name'] . ' Contents';
  30408. $info[$groupindex] = '';
  30409. foreach (array('Package', 'Extension') as $type) {
  30410. $index = strtolower($type);
  30411. if (isset($group[$index])) {
  30412. if (isset($group[$index]['name'])) {
  30413. $group[$index] = array($group[$index]);
  30414. }
  30415. foreach ($group[$index] as $package) {
  30416. if (!empty($info[$groupindex])) {
  30417. $info[$groupindex] .= "\n";
  30418. }
  30419. if ($index == 'extension') {
  30420. $name = $package['name'];
  30421. } else {
  30422. if (isset($package['channel'])) {
  30423. $name = $package['channel'] . '/' . $package['name'];
  30424. } else {
  30425. $name = '__uri/' . $package['name'] . ' (static URI)';
  30426. }
  30427. }
  30428. if (isset($package['uri'])) {
  30429. if (isset($package['conflicts']) && $package['conflicts'] == 'yes') {
  30430. $info[$groupindex] .= "Not Compatible with $type $name";
  30431. } else {
  30432. $info[$groupindex] .= "$type $name";
  30433. }
  30434. $info[$groupindex] .= "\n Download URI: $package[uri]";
  30435. continue;
  30436. }
  30437. if (isset($package['conflicts']) && $package['conflicts'] == 'yes') {
  30438. $info[$groupindex] .= "Not Compatible with $type $name";
  30439. continue;
  30440. }
  30441. $info[$groupindex] .= "$type $name";
  30442. if (isset($package['max']) && isset($package['min'])) {
  30443. $info[$groupindex] .= " \n Versions " .
  30444. $package['min'] . '-' . $package['max'];
  30445. } elseif (isset($package['min'])) {
  30446. $info[$groupindex] .= " \n Version " .
  30447. $package['min'] . ' or newer';
  30448. } elseif (isset($package['max'])) {
  30449. $info[$groupindex] .= " \n Version " .
  30450. $package['min'] . ' or older';
  30451. }
  30452. if (isset($package['recommended'])) {
  30453. $info[$groupindex] .= "\n Recommended version: $package[recommended]";
  30454. }
  30455. if (isset($package['exclude'])) {
  30456. if (!isset($info['Not Compatible with'])) {
  30457. $info['Not Compatible with'] = '';
  30458. } else {
  30459. $info[$groupindex] .= "Not Compatible with\n";
  30460. }
  30461. if (is_array($package['exclude'])) {
  30462. $package['exclude'] = implode(', ', $package['exclude']);
  30463. }
  30464. $info[$groupindex] .= " Package $package\n Versions " .
  30465. $package['exclude'];
  30466. }
  30467. }
  30468. }
  30469. }
  30470. }
  30471. }
  30472. if ($obj->getPackageType() == 'bundle') {
  30473. $info['Bundled Packages'] = '';
  30474. foreach ($obj->getBundledPackages() as $package) {
  30475. if (!empty($info['Bundled Packages'])) {
  30476. $info['Bundled Packages'] .= "\n";
  30477. }
  30478. if (isset($package['uri'])) {
  30479. $info['Bundled Packages'] .= '__uri/' . $package['name'];
  30480. $info['Bundled Packages'] .= "\n (URI: $package[uri]";
  30481. } else {
  30482. $info['Bundled Packages'] .= $package['channel'] . '/' . $package['name'];
  30483. }
  30484. }
  30485. }
  30486. $info['package.xml version'] = '2.0';
  30487. if ($installed) {
  30488. if ($obj->getLastModified()) {
  30489. $info['Last Modified'] = date('Y-m-d H:i', $obj->getLastModified());
  30490. }
  30491. $v = $obj->getLastInstalledVersion();
  30492. $info['Previous Installed Version'] = $v ? $v : '- None -';
  30493. }
  30494. foreach ($info as $key => $value) {
  30495. $data['data'][] = array($key, $value);
  30496. }
  30497. $data['raw'] = $obj->getArray(); // no validation needed
  30498. $this->ui->outputData($data, 'package-info');
  30499. }
  30500. }
  30501. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Remote.xml����������������������������������������������������������������0000644�0001750�0001750�00000006357�14720722517�016363� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<commands version="1.0">
  30502. <remote-info>
  30503. <summary>Information About Remote Packages</summary>
  30504. <function>doRemoteInfo</function>
  30505. <shortcut>ri</shortcut>
  30506. <options />
  30507. <doc>&lt;package&gt;
  30508. Get details on a package from the server.</doc>
  30509. </remote-info>
  30510. <list-upgrades>
  30511. <summary>List Available Upgrades</summary>
  30512. <function>doListUpgrades</function>
  30513. <shortcut>lu</shortcut>
  30514. <options>
  30515. <channelinfo>
  30516. <shortopt>i</shortopt>
  30517. <doc>output fully channel-aware data, even on failure</doc>
  30518. </channelinfo>
  30519. </options>
  30520. <doc>[preferred_state]
  30521. List releases on the server of packages you have installed where
  30522. a newer version is available with the same release state (stable etc.)
  30523. or the state passed as the second parameter.</doc>
  30524. </list-upgrades>
  30525. <remote-list>
  30526. <summary>List Remote Packages</summary>
  30527. <function>doRemoteList</function>
  30528. <shortcut>rl</shortcut>
  30529. <options>
  30530. <channel>
  30531. <shortopt>c</shortopt>
  30532. <doc>specify a channel other than the default channel</doc>
  30533. <arg>CHAN</arg>
  30534. </channel>
  30535. </options>
  30536. <doc>
  30537. Lists the packages available on the configured server along with the
  30538. latest stable release of each package.</doc>
  30539. </remote-list>
  30540. <search>
  30541. <summary>Search remote package database</summary>
  30542. <function>doSearch</function>
  30543. <shortcut>sp</shortcut>
  30544. <options>
  30545. <channel>
  30546. <shortopt>c</shortopt>
  30547. <doc>specify a channel other than the default channel</doc>
  30548. <arg>CHAN</arg>
  30549. </channel>
  30550. <allchannels>
  30551. <shortopt>a</shortopt>
  30552. <doc>search packages from all known channels</doc>
  30553. </allchannels>
  30554. <channelinfo>
  30555. <shortopt>i</shortopt>
  30556. <doc>output fully channel-aware data, even on failure</doc>
  30557. </channelinfo>
  30558. </options>
  30559. <doc>[packagename] [packageinfo]
  30560. Lists all packages which match the search parameters. The first
  30561. parameter is a fragment of a packagename. The default channel
  30562. will be used unless explicitly overridden. The second parameter
  30563. will be used to match any portion of the summary/description</doc>
  30564. </search>
  30565. <list-all>
  30566. <summary>List All Packages</summary>
  30567. <function>doListAll</function>
  30568. <shortcut>la</shortcut>
  30569. <options>
  30570. <channel>
  30571. <shortopt>c</shortopt>
  30572. <doc>specify a channel other than the default channel</doc>
  30573. <arg>CHAN</arg>
  30574. </channel>
  30575. <channelinfo>
  30576. <shortopt>i</shortopt>
  30577. <doc>output fully channel-aware data, even on failure</doc>
  30578. </channelinfo>
  30579. </options>
  30580. <doc>
  30581. Lists the packages available on the configured server along with the
  30582. latest stable release of each package.</doc>
  30583. </list-all>
  30584. <download>
  30585. <summary>Download Package</summary>
  30586. <function>doDownload</function>
  30587. <shortcut>d</shortcut>
  30588. <options>
  30589. <nocompress>
  30590. <shortopt>Z</shortopt>
  30591. <doc>download an uncompressed (.tar) file</doc>
  30592. </nocompress>
  30593. </options>
  30594. <doc>&lt;package&gt;...
  30595. Download package tarballs. The files will be named as suggested by the
  30596. server, for example if you download the DB package and the latest stable
  30597. version of DB is 1.6.5, the downloaded file will be DB-1.6.5.tgz.</doc>
  30598. </download>
  30599. <clear-cache>
  30600. <summary>Clear Web Services Cache</summary>
  30601. <function>doClearCache</function>
  30602. <shortcut>cc</shortcut>
  30603. <options />
  30604. <doc>
  30605. Clear the XML-RPC/REST cache. See also the cache_ttl configuration
  30606. parameter.
  30607. </doc>
  30608. </clear-cache>
  30609. </commands>���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Remote.php����������������������������������������������������������������0000664�0001750�0001750�00000072442�14720722517�016352� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  30610. /**
  30611. * PEAR_Command_Remote (remote-info, list-upgrades, remote-list, search, list-all, download,
  30612. * clear-cache commands)
  30613. *
  30614. * PHP versions 4 and 5
  30615. *
  30616. * @category pear
  30617. * @package PEAR
  30618. * @author Stig Bakken <ssb@php.net>
  30619. * @author Greg Beaver <cellog@php.net>
  30620. * @copyright 1997-2009 The Authors
  30621. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  30622. * @link http://pear.php.net/package/PEAR
  30623. * @since File available since Release 0.1
  30624. */
  30625. /**
  30626. * base class
  30627. */
  30628. require_once 'PEAR/Command/Common.php';
  30629. require_once 'PEAR/REST.php';
  30630. /**
  30631. * PEAR commands for remote server querying
  30632. *
  30633. * @category pear
  30634. * @package PEAR
  30635. * @author Stig Bakken <ssb@php.net>
  30636. * @author Greg Beaver <cellog@php.net>
  30637. * @copyright 1997-2009 The Authors
  30638. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  30639. * @version Release: 1.10.16
  30640. * @link http://pear.php.net/package/PEAR
  30641. * @since Class available since Release 0.1
  30642. */
  30643. class PEAR_Command_Remote extends PEAR_Command_Common
  30644. {
  30645. var $commands = array(
  30646. 'remote-info' => array(
  30647. 'summary' => 'Information About Remote Packages',
  30648. 'function' => 'doRemoteInfo',
  30649. 'shortcut' => 'ri',
  30650. 'options' => array(),
  30651. 'doc' => '<package>
  30652. Get details on a package from the server.',
  30653. ),
  30654. 'list-upgrades' => array(
  30655. 'summary' => 'List Available Upgrades',
  30656. 'function' => 'doListUpgrades',
  30657. 'shortcut' => 'lu',
  30658. 'options' => array(
  30659. 'channelinfo' => array(
  30660. 'shortopt' => 'i',
  30661. 'doc' => 'output fully channel-aware data, even on failure',
  30662. ),
  30663. ),
  30664. 'doc' => '[preferred_state]
  30665. List releases on the server of packages you have installed where
  30666. a newer version is available with the same release state (stable etc.)
  30667. or the state passed as the second parameter.'
  30668. ),
  30669. 'remote-list' => array(
  30670. 'summary' => 'List Remote Packages',
  30671. 'function' => 'doRemoteList',
  30672. 'shortcut' => 'rl',
  30673. 'options' => array(
  30674. 'channel' =>
  30675. array(
  30676. 'shortopt' => 'c',
  30677. 'doc' => 'specify a channel other than the default channel',
  30678. 'arg' => 'CHAN',
  30679. )
  30680. ),
  30681. 'doc' => '
  30682. Lists the packages available on the configured server along with the
  30683. latest stable release of each package.',
  30684. ),
  30685. 'search' => array(
  30686. 'summary' => 'Search remote package database',
  30687. 'function' => 'doSearch',
  30688. 'shortcut' => 'sp',
  30689. 'options' => array(
  30690. 'channel' =>
  30691. array(
  30692. 'shortopt' => 'c',
  30693. 'doc' => 'specify a channel other than the default channel',
  30694. 'arg' => 'CHAN',
  30695. ),
  30696. 'allchannels' => array(
  30697. 'shortopt' => 'a',
  30698. 'doc' => 'search packages from all known channels',
  30699. ),
  30700. 'channelinfo' => array(
  30701. 'shortopt' => 'i',
  30702. 'doc' => 'output fully channel-aware data, even on failure',
  30703. ),
  30704. ),
  30705. 'doc' => '[packagename] [packageinfo]
  30706. Lists all packages which match the search parameters. The first
  30707. parameter is a fragment of a packagename. The default channel
  30708. will be used unless explicitly overridden. The second parameter
  30709. will be used to match any portion of the summary/description',
  30710. ),
  30711. 'list-all' => array(
  30712. 'summary' => 'List All Packages',
  30713. 'function' => 'doListAll',
  30714. 'shortcut' => 'la',
  30715. 'options' => array(
  30716. 'channel' =>
  30717. array(
  30718. 'shortopt' => 'c',
  30719. 'doc' => 'specify a channel other than the default channel',
  30720. 'arg' => 'CHAN',
  30721. ),
  30722. 'channelinfo' => array(
  30723. 'shortopt' => 'i',
  30724. 'doc' => 'output fully channel-aware data, even on failure',
  30725. ),
  30726. ),
  30727. 'doc' => '
  30728. Lists the packages available on the configured server along with the
  30729. latest stable release of each package.',
  30730. ),
  30731. 'download' => array(
  30732. 'summary' => 'Download Package',
  30733. 'function' => 'doDownload',
  30734. 'shortcut' => 'd',
  30735. 'options' => array(
  30736. 'nocompress' => array(
  30737. 'shortopt' => 'Z',
  30738. 'doc' => 'download an uncompressed (.tar) file',
  30739. ),
  30740. ),
  30741. 'doc' => '<package>...
  30742. Download package tarballs. The files will be named as suggested by the
  30743. server, for example if you download the DB package and the latest stable
  30744. version of DB is 1.6.5, the downloaded file will be DB-1.6.5.tgz.',
  30745. ),
  30746. 'clear-cache' => array(
  30747. 'summary' => 'Clear Web Services Cache',
  30748. 'function' => 'doClearCache',
  30749. 'shortcut' => 'cc',
  30750. 'options' => array(),
  30751. 'doc' => '
  30752. Clear the REST cache. See also the cache_ttl configuration
  30753. parameter.
  30754. ',
  30755. ),
  30756. );
  30757. /**
  30758. * PEAR_Command_Remote constructor.
  30759. *
  30760. * @access public
  30761. */
  30762. function __construct(&$ui, &$config)
  30763. {
  30764. parent::__construct($ui, $config);
  30765. }
  30766. function _checkChannelForStatus($channel, $chan)
  30767. {
  30768. if (PEAR::isError($chan)) {
  30769. $this->raiseError($chan);
  30770. }
  30771. if (!is_a($chan, 'PEAR_ChannelFile')) {
  30772. return $this->raiseError('Internal corruption error: invalid channel "' .
  30773. $channel . '"');
  30774. }
  30775. $rest = new PEAR_REST($this->config);
  30776. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  30777. $mirror = $this->config->get('preferred_mirror', null,
  30778. $channel);
  30779. $a = $rest->downloadHttp('http://' . $channel .
  30780. '/channel.xml', $chan->lastModified());
  30781. PEAR::staticPopErrorHandling();
  30782. if (!PEAR::isError($a) && $a) {
  30783. $this->ui->outputData('WARNING: channel "' . $channel . '" has ' .
  30784. 'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $channel .
  30785. '" to update');
  30786. }
  30787. }
  30788. function doRemoteInfo($command, $options, $params)
  30789. {
  30790. if (sizeof($params) != 1) {
  30791. return $this->raiseError("$command expects one param: the remote package name");
  30792. }
  30793. $savechannel = $channel = $this->config->get('default_channel');
  30794. $reg = &$this->config->getRegistry();
  30795. $package = $params[0];
  30796. $parsed = $reg->parsePackageName($package, $channel);
  30797. if (PEAR::isError($parsed)) {
  30798. return $this->raiseError('Invalid package name "' . $package . '"');
  30799. }
  30800. $channel = $parsed['channel'];
  30801. $this->config->set('default_channel', $channel);
  30802. $chan = $reg->getChannel($channel);
  30803. if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
  30804. return $e;
  30805. }
  30806. $mirror = $this->config->get('preferred_mirror');
  30807. if ($chan->supportsREST($mirror) && $base = $chan->getBaseURL('REST1.0', $mirror)) {
  30808. $rest = &$this->config->getREST('1.0', array());
  30809. $info = $rest->packageInfo($base, $parsed['package'], $channel);
  30810. }
  30811. if (!isset($info)) {
  30812. return $this->raiseError('No supported protocol was found');
  30813. }
  30814. if (PEAR::isError($info)) {
  30815. $this->config->set('default_channel', $savechannel);
  30816. return $this->raiseError($info);
  30817. }
  30818. if (!isset($info['name'])) {
  30819. return $this->raiseError('No remote package "' . $package . '" was found');
  30820. }
  30821. $installed = $reg->packageInfo($info['name'], null, $channel);
  30822. $info['installed'] = $installed ? $installed['version'] : '- no -';
  30823. if (is_array($info['installed'])) {
  30824. $info['installed'] = $info['installed']['release'];
  30825. }
  30826. $this->ui->outputData($info, $command);
  30827. $this->config->set('default_channel', $savechannel);
  30828. return true;
  30829. }
  30830. function doRemoteList($command, $options, $params)
  30831. {
  30832. $savechannel = $channel = $this->config->get('default_channel');
  30833. $reg = &$this->config->getRegistry();
  30834. if (isset($options['channel'])) {
  30835. $channel = $options['channel'];
  30836. if (!$reg->channelExists($channel)) {
  30837. return $this->raiseError('Channel "' . $channel . '" does not exist');
  30838. }
  30839. $this->config->set('default_channel', $channel);
  30840. }
  30841. $chan = $reg->getChannel($channel);
  30842. if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
  30843. return $e;
  30844. }
  30845. $list_options = false;
  30846. if ($this->config->get('preferred_state') == 'stable') {
  30847. $list_options = true;
  30848. }
  30849. $available = array();
  30850. if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
  30851. $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror'))
  30852. ) {
  30853. // use faster list-all if available
  30854. $rest = &$this->config->getREST('1.1', array());
  30855. $available = $rest->listAll($base, $list_options, true, false, false, $chan->getName());
  30856. } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) &&
  30857. $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
  30858. $rest = &$this->config->getREST('1.0', array());
  30859. $available = $rest->listAll($base, $list_options, true, false, false, $chan->getName());
  30860. }
  30861. if (PEAR::isError($available)) {
  30862. $this->config->set('default_channel', $savechannel);
  30863. return $this->raiseError($available);
  30864. }
  30865. $i = $j = 0;
  30866. $data = array(
  30867. 'caption' => 'Channel ' . $channel . ' Available packages:',
  30868. 'border' => true,
  30869. 'headline' => array('Package', 'Version'),
  30870. 'channel' => $channel
  30871. );
  30872. if (count($available) == 0) {
  30873. $data = '(no packages available yet)';
  30874. } else {
  30875. foreach ($available as $name => $info) {
  30876. $version = (isset($info['stable']) && $info['stable']) ? $info['stable'] : '-n/a-';
  30877. $data['data'][] = array($name, $version);
  30878. }
  30879. }
  30880. $this->ui->outputData($data, $command);
  30881. $this->config->set('default_channel', $savechannel);
  30882. return true;
  30883. }
  30884. function doListAll($command, $options, $params)
  30885. {
  30886. $savechannel = $channel = $this->config->get('default_channel');
  30887. $reg = &$this->config->getRegistry();
  30888. if (isset($options['channel'])) {
  30889. $channel = $options['channel'];
  30890. if (!$reg->channelExists($channel)) {
  30891. return $this->raiseError("Channel \"$channel\" does not exist");
  30892. }
  30893. $this->config->set('default_channel', $channel);
  30894. }
  30895. $list_options = false;
  30896. if ($this->config->get('preferred_state') == 'stable') {
  30897. $list_options = true;
  30898. }
  30899. $chan = $reg->getChannel($channel);
  30900. if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
  30901. return $e;
  30902. }
  30903. if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
  30904. $base = $chan->getBaseURL('REST1.1', $this->config->get('preferred_mirror'))) {
  30905. // use faster list-all if available
  30906. $rest = &$this->config->getREST('1.1', array());
  30907. $available = $rest->listAll($base, $list_options, false, false, false, $chan->getName());
  30908. } elseif ($chan->supportsREST($this->config->get('preferred_mirror')) &&
  30909. $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
  30910. $rest = &$this->config->getREST('1.0', array());
  30911. $available = $rest->listAll($base, $list_options, false, false, false, $chan->getName());
  30912. }
  30913. if (PEAR::isError($available)) {
  30914. $this->config->set('default_channel', $savechannel);
  30915. return $this->raiseError('The package list could not be fetched from the remote server. Please try again. (Debug info: "' . $available->getMessage() . '")');
  30916. }
  30917. $data = array(
  30918. 'caption' => 'All packages [Channel ' . $channel . ']:',
  30919. 'border' => true,
  30920. 'headline' => array('Package', 'Latest', 'Local'),
  30921. 'channel' => $channel,
  30922. );
  30923. if (isset($options['channelinfo'])) {
  30924. // add full channelinfo
  30925. $data['caption'] = 'Channel ' . $channel . ' All packages:';
  30926. $data['headline'] = array('Channel', 'Package', 'Latest', 'Local',
  30927. 'Description', 'Dependencies');
  30928. }
  30929. $local_pkgs = $reg->listPackages($channel);
  30930. foreach ($available as $name => $info) {
  30931. $installed = $reg->packageInfo($name, null, $channel);
  30932. if ($installed && is_array($installed['version'])) {
  30933. $installed['version'] = $installed['version']['release'];
  30934. }
  30935. $desc = $info['summary'];
  30936. if (isset($params[$name])) {
  30937. $desc .= "\n\n".$info['description'];
  30938. }
  30939. if (isset($options['mode']))
  30940. {
  30941. if ($options['mode'] == 'installed' && !isset($installed['version'])) {
  30942. continue;
  30943. }
  30944. if ($options['mode'] == 'notinstalled' && isset($installed['version'])) {
  30945. continue;
  30946. }
  30947. if ($options['mode'] == 'upgrades'
  30948. && (!isset($installed['version']) || version_compare($installed['version'],
  30949. $info['stable'], '>='))) {
  30950. continue;
  30951. }
  30952. }
  30953. $pos = array_search(strtolower($name), $local_pkgs);
  30954. if ($pos !== false) {
  30955. unset($local_pkgs[$pos]);
  30956. }
  30957. if (isset($info['stable']) && !$info['stable']) {
  30958. $info['stable'] = null;
  30959. }
  30960. if (isset($options['channelinfo'])) {
  30961. // add full channelinfo
  30962. if ($info['stable'] === $info['unstable']) {
  30963. $state = $info['state'];
  30964. } else {
  30965. $state = 'stable';
  30966. }
  30967. $latest = $info['stable'].' ('.$state.')';
  30968. $local = '';
  30969. if (isset($installed['version'])) {
  30970. $inst_state = $reg->packageInfo($name, 'release_state', $channel);
  30971. $local = $installed['version'].' ('.$inst_state.')';
  30972. }
  30973. $packageinfo = array(
  30974. $channel,
  30975. $name,
  30976. $latest,
  30977. $local,
  30978. isset($desc) ? $desc : null,
  30979. isset($info['deps']) ? $info['deps'] : null,
  30980. );
  30981. } else {
  30982. $packageinfo = array(
  30983. $reg->channelAlias($channel) . '/' . $name,
  30984. isset($info['stable']) ? $info['stable'] : null,
  30985. isset($installed['version']) ? $installed['version'] : null,
  30986. isset($desc) ? $desc : null,
  30987. isset($info['deps']) ? $info['deps'] : null,
  30988. );
  30989. }
  30990. $data['data'][$info['category']][] = $packageinfo;
  30991. }
  30992. if (isset($options['mode']) && in_array($options['mode'], array('notinstalled', 'upgrades'))) {
  30993. $this->config->set('default_channel', $savechannel);
  30994. $this->ui->outputData($data, $command);
  30995. return true;
  30996. }
  30997. foreach ($local_pkgs as $name) {
  30998. $info = &$reg->getPackage($name, $channel);
  30999. $data['data']['Local'][] = array(
  31000. $reg->channelAlias($channel) . '/' . $info->getPackage(),
  31001. '',
  31002. $info->getVersion(),
  31003. $info->getSummary(),
  31004. $info->getDeps()
  31005. );
  31006. }
  31007. $this->config->set('default_channel', $savechannel);
  31008. $this->ui->outputData($data, $command);
  31009. return true;
  31010. }
  31011. function doSearch($command, $options, $params)
  31012. {
  31013. if ((!isset($params[0]) || empty($params[0]))
  31014. && (!isset($params[1]) || empty($params[1])))
  31015. {
  31016. return $this->raiseError('no valid search string supplied');
  31017. }
  31018. $channelinfo = isset($options['channelinfo']);
  31019. $reg = &$this->config->getRegistry();
  31020. if (isset($options['allchannels'])) {
  31021. // search all channels
  31022. unset($options['allchannels']);
  31023. $channels = $reg->getChannels();
  31024. $errors = array();
  31025. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  31026. foreach ($channels as $channel) {
  31027. if ($channel->getName() != '__uri') {
  31028. $options['channel'] = $channel->getName();
  31029. $ret = $this->doSearch($command, $options, $params);
  31030. if (PEAR::isError($ret)) {
  31031. $errors[] = $ret;
  31032. }
  31033. }
  31034. }
  31035. PEAR::staticPopErrorHandling();
  31036. if (count($errors) !== 0) {
  31037. // for now, only give first error
  31038. return PEAR::raiseError($errors[0]);
  31039. }
  31040. return true;
  31041. }
  31042. $savechannel = $channel = $this->config->get('default_channel');
  31043. $package = strtolower($params[0]);
  31044. $summary = isset($params[1]) ? $params[1] : false;
  31045. if (isset($options['channel'])) {
  31046. $reg = &$this->config->getRegistry();
  31047. $channel = $options['channel'];
  31048. if (!$reg->channelExists($channel)) {
  31049. return $this->raiseError('Channel "' . $channel . '" does not exist');
  31050. }
  31051. $this->config->set('default_channel', $channel);
  31052. }
  31053. $chan = $reg->getChannel($channel);
  31054. if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
  31055. return $e;
  31056. }
  31057. if ($chan->supportsREST($this->config->get('preferred_mirror')) &&
  31058. $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))) {
  31059. $rest = &$this->config->getREST('1.0', array());
  31060. $available = $rest->listAll($base, false, false, $package, $summary, $chan->getName());
  31061. }
  31062. if (PEAR::isError($available)) {
  31063. $this->config->set('default_channel', $savechannel);
  31064. return $this->raiseError($available);
  31065. }
  31066. if (!$available && !$channelinfo) {
  31067. // clean exit when not found, no error !
  31068. $data = 'no packages found that match pattern "' . $package . '", for channel '.$channel.'.';
  31069. $this->ui->outputData($data);
  31070. $this->config->set('default_channel', $channel);
  31071. return true;
  31072. }
  31073. if ($channelinfo) {
  31074. $data = array(
  31075. 'caption' => 'Matched packages, channel ' . $channel . ':',
  31076. 'border' => true,
  31077. 'headline' => array('Channel', 'Package', 'Stable/(Latest)', 'Local'),
  31078. 'channel' => $channel
  31079. );
  31080. } else {
  31081. $data = array(
  31082. 'caption' => 'Matched packages, channel ' . $channel . ':',
  31083. 'border' => true,
  31084. 'headline' => array('Package', 'Stable/(Latest)', 'Local'),
  31085. 'channel' => $channel
  31086. );
  31087. }
  31088. if (!$available && $channelinfo) {
  31089. unset($data['headline']);
  31090. $data['data'] = 'No packages found that match pattern "' . $package . '".';
  31091. $available = array();
  31092. }
  31093. foreach ($available as $name => $info) {
  31094. $installed = $reg->packageInfo($name, null, $channel);
  31095. $desc = $info['summary'];
  31096. if (isset($params[$name]))
  31097. $desc .= "\n\n".$info['description'];
  31098. if (!isset($info['stable']) || !$info['stable']) {
  31099. $version_remote = 'none';
  31100. } else {
  31101. if ($info['unstable']) {
  31102. $version_remote = $info['unstable'];
  31103. } else {
  31104. $version_remote = $info['stable'];
  31105. }
  31106. $version_remote .= ' ('.$info['state'].')';
  31107. }
  31108. $version = is_array($installed['version']) ? $installed['version']['release'] :
  31109. $installed['version'];
  31110. if ($channelinfo) {
  31111. $packageinfo = array(
  31112. $channel,
  31113. $name,
  31114. $version_remote,
  31115. $version,
  31116. $desc,
  31117. );
  31118. } else {
  31119. $packageinfo = array(
  31120. $name,
  31121. $version_remote,
  31122. $version,
  31123. $desc,
  31124. );
  31125. }
  31126. $data['data'][$info['category']][] = $packageinfo;
  31127. }
  31128. $this->ui->outputData($data, $command);
  31129. $this->config->set('default_channel', $channel);
  31130. return true;
  31131. }
  31132. function &getDownloader($options)
  31133. {
  31134. if (!class_exists('PEAR_Downloader')) {
  31135. require_once 'PEAR/Downloader.php';
  31136. }
  31137. $a = new PEAR_Downloader($this->ui, $options, $this->config);
  31138. return $a;
  31139. }
  31140. function doDownload($command, $options, $params)
  31141. {
  31142. // make certain that dependencies are ignored
  31143. $options['downloadonly'] = 1;
  31144. // eliminate error messages for preferred_state-related errors
  31145. /* TODO: Should be an option, but until now download does respect
  31146. preferred state */
  31147. /* $options['ignorepreferred_state'] = 1; */
  31148. // eliminate error messages for preferred_state-related errors
  31149. $downloader = &$this->getDownloader($options);
  31150. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  31151. $e = $downloader->setDownloadDir(getcwd());
  31152. PEAR::staticPopErrorHandling();
  31153. if (PEAR::isError($e)) {
  31154. return $this->raiseError('Current directory is not writeable, cannot download');
  31155. }
  31156. $errors = array();
  31157. $downloaded = array();
  31158. $err = $downloader->download($params);
  31159. if (PEAR::isError($err)) {
  31160. return $err;
  31161. }
  31162. $errors = $downloader->getErrorMsgs();
  31163. if (count($errors)) {
  31164. foreach ($errors as $error) {
  31165. if ($error !== null) {
  31166. $this->ui->outputData($error);
  31167. }
  31168. }
  31169. return $this->raiseError("$command failed");
  31170. }
  31171. $downloaded = $downloader->getDownloadedPackages();
  31172. foreach ($downloaded as $pkg) {
  31173. $this->ui->outputData("File $pkg[file] downloaded", $command);
  31174. }
  31175. return true;
  31176. }
  31177. function downloadCallback($msg, $params = null)
  31178. {
  31179. if ($msg == 'done') {
  31180. $this->bytes_downloaded = $params;
  31181. }
  31182. }
  31183. function doListUpgrades($command, $options, $params)
  31184. {
  31185. require_once 'PEAR/Common.php';
  31186. if (isset($params[0]) && !is_array(PEAR_Common::betterStates($params[0]))) {
  31187. return $this->raiseError($params[0] . ' is not a valid state (stable/beta/alpha/devel/etc.) try "pear help list-upgrades"');
  31188. }
  31189. $savechannel = $channel = $this->config->get('default_channel');
  31190. $reg = &$this->config->getRegistry();
  31191. foreach ($reg->listChannels() as $channel) {
  31192. $inst = array_flip($reg->listPackages($channel));
  31193. if (!count($inst)) {
  31194. continue;
  31195. }
  31196. if ($channel == '__uri') {
  31197. continue;
  31198. }
  31199. $this->config->set('default_channel', $channel);
  31200. $state = empty($params[0]) ? $this->config->get('preferred_state') : $params[0];
  31201. $caption = $channel . ' Available Upgrades';
  31202. $chan = $reg->getChannel($channel);
  31203. if (PEAR::isError($e = $this->_checkChannelForStatus($channel, $chan))) {
  31204. return $e;
  31205. }
  31206. $latest = array();
  31207. $base2 = false;
  31208. $preferred_mirror = $this->config->get('preferred_mirror');
  31209. if ($chan->supportsREST($preferred_mirror) &&
  31210. (
  31211. ($base2 = $chan->getBaseURL('REST1.3', $preferred_mirror))
  31212. || ($base = $chan->getBaseURL('REST1.0', $preferred_mirror))
  31213. )
  31214. ) {
  31215. if ($base2) {
  31216. $rest = &$this->config->getREST('1.3', array());
  31217. $base = $base2;
  31218. } else {
  31219. $rest = &$this->config->getREST('1.0', array());
  31220. }
  31221. if (empty($state) || $state == 'any') {
  31222. $state = false;
  31223. } else {
  31224. $caption .= ' (' . implode(', ', PEAR_Common::betterStates($state, true)) . ')';
  31225. }
  31226. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  31227. $latest = $rest->listLatestUpgrades($base, $state, $inst, $channel, $reg);
  31228. PEAR::staticPopErrorHandling();
  31229. }
  31230. if (PEAR::isError($latest)) {
  31231. $this->ui->outputData($latest->getMessage());
  31232. continue;
  31233. }
  31234. $caption .= ':';
  31235. if (PEAR::isError($latest)) {
  31236. $this->config->set('default_channel', $savechannel);
  31237. return $latest;
  31238. }
  31239. $data = array(
  31240. 'caption' => $caption,
  31241. 'border' => 1,
  31242. 'headline' => array('Channel', 'Package', 'Local', 'Remote', 'Size'),
  31243. 'channel' => $channel
  31244. );
  31245. foreach ((array)$latest as $pkg => $info) {
  31246. $package = strtolower($pkg);
  31247. if (!isset($inst[$package])) {
  31248. // skip packages we don't have installed
  31249. continue;
  31250. }
  31251. extract($info);
  31252. $inst_version = $reg->packageInfo($package, 'version', $channel);
  31253. $inst_state = $reg->packageInfo($package, 'release_state', $channel);
  31254. if (version_compare("$version", "$inst_version", "le")) {
  31255. // installed version is up-to-date
  31256. continue;
  31257. }
  31258. if ($filesize >= 20480) {
  31259. $filesize += 1024 - ($filesize % 1024);
  31260. $fs = sprintf("%dkB", $filesize / 1024);
  31261. } elseif ($filesize > 0) {
  31262. $filesize += 103 - ($filesize % 103);
  31263. $fs = sprintf("%.1fkB", $filesize / 1024.0);
  31264. } else {
  31265. $fs = " -"; // XXX center instead
  31266. }
  31267. $data['data'][] = array($channel, $pkg, "$inst_version ($inst_state)", "$version ($state)", $fs);
  31268. }
  31269. if (isset($options['channelinfo'])) {
  31270. if (empty($data['data'])) {
  31271. unset($data['headline']);
  31272. if (count($inst) == 0) {
  31273. $data['data'] = '(no packages installed)';
  31274. } else {
  31275. $data['data'] = '(no upgrades available)';
  31276. }
  31277. }
  31278. $this->ui->outputData($data, $command);
  31279. } else {
  31280. if (empty($data['data'])) {
  31281. $this->ui->outputData('Channel ' . $channel . ': No upgrades available');
  31282. } else {
  31283. $this->ui->outputData($data, $command);
  31284. }
  31285. }
  31286. }
  31287. $this->config->set('default_channel', $savechannel);
  31288. return true;
  31289. }
  31290. function doClearCache($command, $options, $params)
  31291. {
  31292. $cache_dir = $this->config->get('cache_dir');
  31293. $verbose = $this->config->get('verbose');
  31294. $output = '';
  31295. if (!file_exists($cache_dir) || !is_dir($cache_dir)) {
  31296. return $this->raiseError("$cache_dir does not exist or is not a directory");
  31297. }
  31298. if (!($dp = @opendir($cache_dir))) {
  31299. return $this->raiseError("opendir($cache_dir) failed: $php_errormsg");
  31300. }
  31301. if ($verbose >= 1) {
  31302. $output .= "reading directory $cache_dir\n";
  31303. }
  31304. $num = 0;
  31305. while ($ent = readdir($dp)) {
  31306. if (preg_match('/rest.cache(file|id)\\z/', $ent)) {
  31307. $path = $cache_dir . DIRECTORY_SEPARATOR . $ent;
  31308. if (file_exists($path)) {
  31309. $ok = @unlink($path);
  31310. } else {
  31311. $ok = false;
  31312. $php_errormsg = '';
  31313. }
  31314. if ($ok) {
  31315. if ($verbose >= 2) {
  31316. $output .= "deleted $path\n";
  31317. }
  31318. $num++;
  31319. } elseif ($verbose >= 1) {
  31320. $output .= "failed to delete $path $php_errormsg\n";
  31321. }
  31322. }
  31323. }
  31324. closedir($dp);
  31325. if ($verbose >= 1) {
  31326. $output .= "$num cache entries cleared\n";
  31327. }
  31328. $this->ui->outputData(rtrim($output), $command);
  31329. return $num;
  31330. }
  31331. }
  31332. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Test.xml������������������������������������������������������������������0000644�0001750�0001750�00000003151�14720722517�016034� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<commands version="1.0">
  31333. <run-tests>
  31334. <summary>Run Regression Tests</summary>
  31335. <function>doRunTests</function>
  31336. <shortcut>rt</shortcut>
  31337. <options>
  31338. <recur>
  31339. <shortopt>r</shortopt>
  31340. <doc>Run tests in child directories, recursively. 4 dirs deep maximum</doc>
  31341. </recur>
  31342. <ini>
  31343. <shortopt>i</shortopt>
  31344. <doc>actual string of settings to pass to php in format &quot; -d setting=blah&quot;</doc>
  31345. <arg>SETTINGS</arg>
  31346. </ini>
  31347. <realtimelog>
  31348. <shortopt>l</shortopt>
  31349. <doc>Log test runs/results as they are run</doc>
  31350. </realtimelog>
  31351. <quiet>
  31352. <shortopt>q</shortopt>
  31353. <doc>Only display detail for failed tests</doc>
  31354. </quiet>
  31355. <simple>
  31356. <shortopt>s</shortopt>
  31357. <doc>Display simple output for all tests</doc>
  31358. </simple>
  31359. <package>
  31360. <shortopt>p</shortopt>
  31361. <doc>Treat parameters as installed packages from which to run tests</doc>
  31362. </package>
  31363. <phpunit>
  31364. <shortopt>u</shortopt>
  31365. <doc>Search parameters for AllTests.php, and use that to run phpunit-based tests
  31366. If none is found, all .phpt tests will be tried instead.</doc>
  31367. </phpunit>
  31368. <tapoutput>
  31369. <shortopt>t</shortopt>
  31370. <doc>Output run-tests.log in TAP-compliant format</doc>
  31371. </tapoutput>
  31372. <cgi>
  31373. <shortopt>c</shortopt>
  31374. <doc>CGI php executable (needed for tests with POST/GET section)</doc>
  31375. <arg>PHPCGI</arg>
  31376. </cgi>
  31377. <coverage>
  31378. <shortopt>x</shortopt>
  31379. <doc>Generate a code coverage report (requires Xdebug 2.0.0+)</doc>
  31380. </coverage>
  31381. </options>
  31382. <doc>[testfile|dir ...]
  31383. Run regression tests with PHP&#039;s regression testing script (run-tests.php).</doc>
  31384. </run-tests>
  31385. </commands>�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command/Test.php������������������������������������������������������������������0000644�0001750�0001750�00000027550�14720722517�016034� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  31386. /**
  31387. * PEAR_Command_Test (run-tests)
  31388. *
  31389. * PHP versions 4 and 5
  31390. *
  31391. * @category pear
  31392. * @package PEAR
  31393. * @author Stig Bakken <ssb@php.net>
  31394. * @author Martin Jansen <mj@php.net>
  31395. * @author Greg Beaver <cellog@php.net>
  31396. * @copyright 1997-2009 The Authors
  31397. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  31398. * @link http://pear.php.net/package/PEAR
  31399. * @since File available since Release 0.1
  31400. */
  31401. /**
  31402. * base class
  31403. */
  31404. require_once 'PEAR/Command/Common.php';
  31405. /**
  31406. * PEAR commands for login/logout
  31407. *
  31408. * @category pear
  31409. * @package PEAR
  31410. * @author Stig Bakken <ssb@php.net>
  31411. * @author Martin Jansen <mj@php.net>
  31412. * @author Greg Beaver <cellog@php.net>
  31413. * @copyright 1997-2009 The Authors
  31414. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  31415. * @version Release: 1.10.16
  31416. * @link http://pear.php.net/package/PEAR
  31417. * @since Class available since Release 0.1
  31418. */
  31419. class PEAR_Command_Test extends PEAR_Command_Common
  31420. {
  31421. var $commands = array(
  31422. 'run-tests' => array(
  31423. 'summary' => 'Run Regression Tests',
  31424. 'function' => 'doRunTests',
  31425. 'shortcut' => 'rt',
  31426. 'options' => array(
  31427. 'recur' => array(
  31428. 'shortopt' => 'r',
  31429. 'doc' => 'Run tests in child directories, recursively. 4 dirs deep maximum',
  31430. ),
  31431. 'ini' => array(
  31432. 'shortopt' => 'i',
  31433. 'doc' => 'actual string of settings to pass to php in format " -d setting=blah"',
  31434. 'arg' => 'SETTINGS'
  31435. ),
  31436. 'realtimelog' => array(
  31437. 'shortopt' => 'l',
  31438. 'doc' => 'Log test runs/results as they are run',
  31439. ),
  31440. 'quiet' => array(
  31441. 'shortopt' => 'q',
  31442. 'doc' => 'Only display detail for failed tests',
  31443. ),
  31444. 'simple' => array(
  31445. 'shortopt' => 's',
  31446. 'doc' => 'Display simple output for all tests',
  31447. ),
  31448. 'package' => array(
  31449. 'shortopt' => 'p',
  31450. 'doc' => 'Treat parameters as installed packages from which to run tests',
  31451. ),
  31452. 'phpunit' => array(
  31453. 'shortopt' => 'u',
  31454. 'doc' => 'Search parameters for AllTests.php, and use that to run phpunit-based tests
  31455. If none is found, all .phpt tests will be tried instead.',
  31456. ),
  31457. 'tapoutput' => array(
  31458. 'shortopt' => 't',
  31459. 'doc' => 'Output run-tests.log in TAP-compliant format',
  31460. ),
  31461. 'cgi' => array(
  31462. 'shortopt' => 'c',
  31463. 'doc' => 'CGI php executable (needed for tests with POST/GET section)',
  31464. 'arg' => 'PHPCGI',
  31465. ),
  31466. 'coverage' => array(
  31467. 'shortopt' => 'x',
  31468. 'doc' => 'Generate a code coverage report (requires Xdebug 2.0.0+)',
  31469. ),
  31470. 'showdiff' => array(
  31471. 'shortopt' => 'd',
  31472. 'doc' => 'Output diff on test failure',
  31473. ),
  31474. ),
  31475. 'doc' => '[testfile|dir ...]
  31476. Run regression tests with PHP\'s regression testing script (run-tests.php).',
  31477. ),
  31478. );
  31479. var $output;
  31480. /**
  31481. * PEAR_Command_Test constructor.
  31482. *
  31483. * @access public
  31484. */
  31485. function __construct(&$ui, &$config)
  31486. {
  31487. parent::__construct($ui, $config);
  31488. }
  31489. function doRunTests($command, $options, $params)
  31490. {
  31491. if (isset($options['phpunit']) && isset($options['tapoutput'])) {
  31492. return $this->raiseError('ERROR: cannot use both --phpunit and --tapoutput at the same time');
  31493. }
  31494. require_once 'PEAR/Common.php';
  31495. require_once 'System.php';
  31496. $log = new PEAR_Common;
  31497. $log->ui = &$this->ui; // slightly hacky, but it will work
  31498. $tests = array();
  31499. $depth = isset($options['recur']) ? 14 : 1;
  31500. if (!count($params)) {
  31501. $params[] = '.';
  31502. }
  31503. if (isset($options['package'])) {
  31504. $oldparams = $params;
  31505. $params = array();
  31506. $reg = &$this->config->getRegistry();
  31507. foreach ($oldparams as $param) {
  31508. $pname = $reg->parsePackageName($param, $this->config->get('default_channel'));
  31509. if (PEAR::isError($pname)) {
  31510. return $this->raiseError($pname);
  31511. }
  31512. $package = &$reg->getPackage($pname['package'], $pname['channel']);
  31513. if (!$package) {
  31514. return PEAR::raiseError('Unknown package "' .
  31515. $reg->parsedPackageNameToString($pname) . '"');
  31516. }
  31517. $filelist = $package->getFilelist();
  31518. foreach ($filelist as $name => $atts) {
  31519. if (isset($atts['role']) && $atts['role'] != 'test') {
  31520. continue;
  31521. }
  31522. if (isset($options['phpunit']) && preg_match('/AllTests\.php\\z/i', $name)) {
  31523. $params[] = $atts['installed_as'];
  31524. continue;
  31525. } elseif (!preg_match('/\.phpt\\z/', $name)) {
  31526. continue;
  31527. }
  31528. $params[] = $atts['installed_as'];
  31529. }
  31530. }
  31531. }
  31532. foreach ($params as $p) {
  31533. if (is_dir($p)) {
  31534. if (isset($options['phpunit'])) {
  31535. $dir = System::find(array($p, '-type', 'f',
  31536. '-maxdepth', $depth,
  31537. '-name', 'AllTests.php'));
  31538. if (count($dir)) {
  31539. foreach ($dir as $p) {
  31540. $p = realpath($p);
  31541. if (!count($tests) ||
  31542. (count($tests) && strlen($p) < strlen($tests[0]))) {
  31543. // this is in a higher-level directory, use this one instead.
  31544. $tests = array($p);
  31545. }
  31546. }
  31547. }
  31548. continue;
  31549. }
  31550. $args = array($p, '-type', 'f', '-name', '*.phpt');
  31551. } else {
  31552. if (isset($options['phpunit'])) {
  31553. if (preg_match('/AllTests\.php\\z/i', $p)) {
  31554. $p = realpath($p);
  31555. if (!count($tests) ||
  31556. (count($tests) && strlen($p) < strlen($tests[0]))) {
  31557. // this is in a higher-level directory, use this one instead.
  31558. $tests = array($p);
  31559. }
  31560. }
  31561. continue;
  31562. }
  31563. if (file_exists($p) && preg_match('/\.phpt$/', $p)) {
  31564. $tests[] = $p;
  31565. continue;
  31566. }
  31567. if (!preg_match('/\.phpt\\z/', $p)) {
  31568. $p .= '.phpt';
  31569. }
  31570. $args = array(dirname($p), '-type', 'f', '-name', $p);
  31571. }
  31572. if (!isset($options['recur'])) {
  31573. $args[] = '-maxdepth';
  31574. $args[] = 1;
  31575. }
  31576. $dir = System::find($args);
  31577. $tests = array_merge($tests, $dir);
  31578. }
  31579. $ini_settings = '';
  31580. if (isset($options['ini'])) {
  31581. $ini_settings .= $options['ini'];
  31582. }
  31583. if (isset($_ENV['TEST_PHP_INCLUDE_PATH'])) {
  31584. $ini_settings .= " -d include_path={$_ENV['TEST_PHP_INCLUDE_PATH']}";
  31585. }
  31586. if ($ini_settings) {
  31587. $this->ui->outputData('Using INI settings: "' . $ini_settings . '"');
  31588. }
  31589. $skipped = $passed = $failed = array();
  31590. $tests_count = count($tests);
  31591. $this->ui->outputData('Running ' . $tests_count . ' tests', $command);
  31592. $start = time();
  31593. if (isset($options['realtimelog']) && file_exists('run-tests.log')) {
  31594. unlink('run-tests.log');
  31595. }
  31596. if (isset($options['tapoutput'])) {
  31597. $tap = '1..' . $tests_count . "\n";
  31598. }
  31599. require_once 'PEAR/RunTest.php';
  31600. $run = new PEAR_RunTest($log, $options);
  31601. $run->tests_count = $tests_count;
  31602. if (isset($options['coverage']) && extension_loaded('xdebug')){
  31603. $run->xdebug_loaded = true;
  31604. } else {
  31605. $run->xdebug_loaded = false;
  31606. }
  31607. $j = $i = 1;
  31608. foreach ($tests as $t) {
  31609. if (isset($options['realtimelog'])) {
  31610. $fp = @fopen('run-tests.log', 'a');
  31611. if ($fp) {
  31612. fwrite($fp, "Running test [$i / $tests_count] $t...");
  31613. fclose($fp);
  31614. }
  31615. }
  31616. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  31617. if (isset($options['phpunit'])) {
  31618. $result = $run->runPHPUnit($t, $ini_settings);
  31619. } else {
  31620. $result = $run->run($t, $ini_settings, $j);
  31621. }
  31622. PEAR::staticPopErrorHandling();
  31623. if (PEAR::isError($result)) {
  31624. $this->ui->log($result->getMessage());
  31625. continue;
  31626. }
  31627. if (isset($options['tapoutput'])) {
  31628. $tap .= $result[0] . ' ' . $i . $result[1] . "\n";
  31629. continue;
  31630. }
  31631. if (isset($options['realtimelog'])) {
  31632. $fp = @fopen('run-tests.log', 'a');
  31633. if ($fp) {
  31634. fwrite($fp, "$result\n");
  31635. fclose($fp);
  31636. }
  31637. }
  31638. if ($result == 'FAILED') {
  31639. $failed[] = $t;
  31640. }
  31641. if ($result == 'PASSED') {
  31642. $passed[] = $t;
  31643. }
  31644. if ($result == 'SKIPPED') {
  31645. $skipped[] = $t;
  31646. }
  31647. $j++;
  31648. }
  31649. $total = date('i:s', time() - $start);
  31650. if (isset($options['tapoutput'])) {
  31651. $fp = @fopen('run-tests.log', 'w');
  31652. if ($fp) {
  31653. fwrite($fp, $tap, strlen($tap));
  31654. fclose($fp);
  31655. $this->ui->outputData('wrote TAP-format log to "' .realpath('run-tests.log') .
  31656. '"', $command);
  31657. }
  31658. } else {
  31659. if (count($failed)) {
  31660. $output = "TOTAL TIME: $total\n";
  31661. $output .= count($passed) . " PASSED TESTS\n";
  31662. $output .= count($skipped) . " SKIPPED TESTS\n";
  31663. $output .= count($failed) . " FAILED TESTS:\n";
  31664. foreach ($failed as $failure) {
  31665. $output .= $failure . "\n";
  31666. }
  31667. $mode = isset($options['realtimelog']) ? 'a' : 'w';
  31668. $fp = @fopen('run-tests.log', $mode);
  31669. if ($fp) {
  31670. fwrite($fp, $output, strlen($output));
  31671. fclose($fp);
  31672. $this->ui->outputData('wrote log to "' . realpath('run-tests.log') . '"', $command);
  31673. }
  31674. } elseif (file_exists('run-tests.log') && !is_dir('run-tests.log')) {
  31675. @unlink('run-tests.log');
  31676. }
  31677. }
  31678. $this->ui->outputData('TOTAL TIME: ' . $total);
  31679. $this->ui->outputData(count($passed) . ' PASSED TESTS', $command);
  31680. $this->ui->outputData(count($skipped) . ' SKIPPED TESTS', $command);
  31681. if (count($failed)) {
  31682. $this->ui->outputData(count($failed) . ' FAILED TESTS:', $command);
  31683. foreach ($failed as $failure) {
  31684. $this->ui->outputData($failure, $command);
  31685. }
  31686. }
  31687. if (count($failed) == 0) {
  31688. return true;
  31689. }
  31690. return $this->raiseError('Some tests failed');
  31691. }
  31692. }
  31693. ��������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Downloader/Package.php������������������������������������������������������������0000664�0001750�0001750�00000224657�14720722517�017201� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  31694. /**
  31695. * PEAR_Downloader_Package
  31696. *
  31697. * PHP versions 4 and 5
  31698. *
  31699. * @category pear
  31700. * @package PEAR
  31701. * @author Greg Beaver <cellog@php.net>
  31702. * @copyright 1997-2009 The Authors
  31703. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  31704. * @link http://pear.php.net/package/PEAR
  31705. * @since File available since Release 1.4.0a1
  31706. */
  31707. /**
  31708. * Error code when parameter initialization fails because no releases
  31709. * exist within preferred_state, but releases do exist
  31710. */
  31711. define('PEAR_DOWNLOADER_PACKAGE_STATE', -1003);
  31712. /**
  31713. * Error code when parameter initialization fails because no releases
  31714. * exist that will work with the existing PHP version
  31715. */
  31716. define('PEAR_DOWNLOADER_PACKAGE_PHPVERSION', -1004);
  31717. /**
  31718. * Coordinates download parameters and manages their dependencies
  31719. * prior to downloading them.
  31720. *
  31721. * Input can come from three sources:
  31722. *
  31723. * - local files (archives or package.xml)
  31724. * - remote files (downloadable urls)
  31725. * - abstract package names
  31726. *
  31727. * The first two elements are handled cleanly by PEAR_PackageFile, but the third requires
  31728. * accessing pearweb's xml-rpc interface to determine necessary dependencies, and the
  31729. * format returned of dependencies is slightly different from that used in package.xml.
  31730. *
  31731. * This class hides the differences between these elements, and makes automatic
  31732. * dependency resolution a piece of cake. It also manages conflicts when
  31733. * two classes depend on incompatible dependencies, or differing versions of the same
  31734. * package dependency. In addition, download will not be attempted if the php version is
  31735. * not supported, PEAR installer version is not supported, or non-PECL extensions are not
  31736. * installed.
  31737. * @category pear
  31738. * @package PEAR
  31739. * @author Greg Beaver <cellog@php.net>
  31740. * @copyright 1997-2009 The Authors
  31741. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  31742. * @version Release: 1.10.16
  31743. * @link http://pear.php.net/package/PEAR
  31744. * @since Class available since Release 1.4.0a1
  31745. */
  31746. class PEAR_Downloader_Package
  31747. {
  31748. /**
  31749. * @var PEAR_Downloader
  31750. */
  31751. var $_downloader;
  31752. /**
  31753. * @var PEAR_Config
  31754. */
  31755. var $_config;
  31756. /**
  31757. * @var PEAR_Registry
  31758. */
  31759. var $_registry;
  31760. /**
  31761. * Used to implement packagingroot properly
  31762. * @var PEAR_Registry
  31763. */
  31764. var $_installRegistry;
  31765. /**
  31766. * @var PEAR_PackageFile_v1|PEAR_PackageFile|v2
  31767. */
  31768. var $_packagefile;
  31769. /**
  31770. * @var array
  31771. */
  31772. var $_parsedname;
  31773. /**
  31774. * @var array
  31775. */
  31776. var $_downloadURL;
  31777. /**
  31778. * @var array
  31779. */
  31780. var $_downloadDeps = array();
  31781. /**
  31782. * @var boolean
  31783. */
  31784. var $_valid = false;
  31785. /**
  31786. * @var boolean
  31787. */
  31788. var $_analyzed = false;
  31789. /**
  31790. * if this or a parent package was invoked with Package-state, this is set to the
  31791. * state variable.
  31792. *
  31793. * This allows temporary reassignment of preferred_state for a parent package and all of
  31794. * its dependencies.
  31795. * @var string|false
  31796. */
  31797. var $_explicitState = false;
  31798. /**
  31799. * If this package is invoked with Package#group, this variable will be true
  31800. */
  31801. var $_explicitGroup = false;
  31802. /**
  31803. * Package type local|url
  31804. * @var string
  31805. */
  31806. var $_type;
  31807. /**
  31808. * Contents of package.xml, if downloaded from a remote channel
  31809. * @var string|false
  31810. * @access private
  31811. */
  31812. var $_rawpackagefile;
  31813. /**
  31814. * @var boolean
  31815. * @access private
  31816. */
  31817. var $_validated = false;
  31818. /**
  31819. * @param PEAR_Downloader
  31820. */
  31821. function __construct(&$downloader)
  31822. {
  31823. $this->_downloader = &$downloader;
  31824. $this->_config = &$this->_downloader->config;
  31825. $this->_registry = &$this->_config->getRegistry();
  31826. $options = $downloader->getOptions();
  31827. if (isset($options['packagingroot'])) {
  31828. $this->_config->setInstallRoot($options['packagingroot']);
  31829. $this->_installRegistry = &$this->_config->getRegistry();
  31830. $this->_config->setInstallRoot(false);
  31831. } else {
  31832. $this->_installRegistry = &$this->_registry;
  31833. }
  31834. $this->_valid = $this->_analyzed = false;
  31835. }
  31836. /**
  31837. * Parse the input and determine whether this is a local file, a remote uri, or an
  31838. * abstract package name.
  31839. *
  31840. * This is the heart of the PEAR_Downloader_Package(), and is used in
  31841. * {@link PEAR_Downloader::download()}
  31842. * @param string
  31843. * @return bool|PEAR_Error
  31844. */
  31845. function initialize($param)
  31846. {
  31847. $origErr = $this->_fromFile($param);
  31848. if ($this->_valid) {
  31849. return true;
  31850. }
  31851. $options = $this->_downloader->getOptions();
  31852. if (isset($options['offline'])) {
  31853. if (PEAR::isError($origErr) && !isset($options['soft'])) {
  31854. foreach ($origErr->getUserInfo() as $userInfo) {
  31855. if (isset($userInfo['message'])) {
  31856. $this->_downloader->log(0, $userInfo['message']);
  31857. }
  31858. }
  31859. $this->_downloader->log(0, $origErr->getMessage());
  31860. }
  31861. return PEAR::raiseError('Cannot download non-local package "' . $param . '"');
  31862. }
  31863. $err = $this->_fromUrl($param);
  31864. if (PEAR::isError($err) || !$this->_valid) {
  31865. if ($this->_type == 'url') {
  31866. if (PEAR::isError($err) && !isset($options['soft'])) {
  31867. $this->_downloader->log(0, $err->getMessage());
  31868. }
  31869. return PEAR::raiseError("Invalid or missing remote package file");
  31870. }
  31871. $err = $this->_fromString($param);
  31872. if (PEAR::isError($err) || !$this->_valid) {
  31873. if (PEAR::isError($err) && $err->getCode() == PEAR_DOWNLOADER_PACKAGE_STATE) {
  31874. return false; // instruct the downloader to silently skip
  31875. }
  31876. if (isset($this->_type) && $this->_type == 'local' && PEAR::isError($origErr)) {
  31877. if (is_array($origErr->getUserInfo())) {
  31878. foreach ($origErr->getUserInfo() as $err) {
  31879. if (is_array($err)) {
  31880. $err = $err['message'];
  31881. }
  31882. if (!isset($options['soft'])) {
  31883. $this->_downloader->log(0, $err);
  31884. }
  31885. }
  31886. }
  31887. if (!isset($options['soft'])) {
  31888. $this->_downloader->log(0, $origErr->getMessage());
  31889. }
  31890. if (is_array($param)) {
  31891. $param = $this->_registry->parsedPackageNameToString($param, true);
  31892. }
  31893. if (!isset($options['soft'])) {
  31894. $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file");
  31895. }
  31896. // Passing no message back - already logged above
  31897. return PEAR::raiseError();
  31898. }
  31899. if (PEAR::isError($err) && !isset($options['soft'])) {
  31900. $this->_downloader->log(0, $err->getMessage());
  31901. }
  31902. if (is_array($param)) {
  31903. $param = $this->_registry->parsedPackageNameToString($param, true);
  31904. }
  31905. if (!isset($options['soft'])) {
  31906. $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file");
  31907. }
  31908. // Passing no message back - already logged above
  31909. return PEAR::raiseError();
  31910. }
  31911. }
  31912. return true;
  31913. }
  31914. /**
  31915. * Retrieve any non-local packages
  31916. * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|PEAR_Error
  31917. */
  31918. function &download()
  31919. {
  31920. if (isset($this->_packagefile)) {
  31921. return $this->_packagefile;
  31922. }
  31923. if (isset($this->_downloadURL['url'])) {
  31924. $this->_isvalid = false;
  31925. $info = $this->getParsedPackage();
  31926. foreach ($info as $i => $p) {
  31927. $info[$i] = strtolower($p);
  31928. }
  31929. $err = $this->_fromUrl($this->_downloadURL['url'],
  31930. $this->_registry->parsedPackageNameToString($this->_parsedname, true));
  31931. $newinfo = $this->getParsedPackage();
  31932. foreach ($newinfo as $i => $p) {
  31933. $newinfo[$i] = strtolower($p);
  31934. }
  31935. if ($info != $newinfo) {
  31936. do {
  31937. if ($info['channel'] == 'pecl.php.net' && $newinfo['channel'] == 'pear.php.net') {
  31938. $info['channel'] = 'pear.php.net';
  31939. if ($info == $newinfo) {
  31940. // skip the channel check if a pecl package says it's a PEAR package
  31941. break;
  31942. }
  31943. }
  31944. if ($info['channel'] == 'pear.php.net' && $newinfo['channel'] == 'pecl.php.net') {
  31945. $info['channel'] = 'pecl.php.net';
  31946. if ($info == $newinfo) {
  31947. // skip the channel check if a pecl package says it's a PEAR package
  31948. break;
  31949. }
  31950. }
  31951. return PEAR::raiseError('CRITICAL ERROR: We are ' .
  31952. $this->_registry->parsedPackageNameToString($info) . ', but the file ' .
  31953. 'downloaded claims to be ' .
  31954. $this->_registry->parsedPackageNameToString($this->getParsedPackage()));
  31955. } while (false);
  31956. }
  31957. if (PEAR::isError($err) || !$this->_valid) {
  31958. return $err;
  31959. }
  31960. }
  31961. $this->_type = 'local';
  31962. return $this->_packagefile;
  31963. }
  31964. function &getPackageFile()
  31965. {
  31966. return $this->_packagefile;
  31967. }
  31968. function &getDownloader()
  31969. {
  31970. return $this->_downloader;
  31971. }
  31972. function getType()
  31973. {
  31974. return $this->_type;
  31975. }
  31976. /**
  31977. * Like {@link initialize()}, but operates on a dependency
  31978. */
  31979. function fromDepURL($dep)
  31980. {
  31981. $this->_downloadURL = $dep;
  31982. if (isset($dep['uri'])) {
  31983. $options = $this->_downloader->getOptions();
  31984. if (!extension_loaded("zlib") || isset($options['nocompress'])) {
  31985. $ext = '.tar';
  31986. } else {
  31987. $ext = '.tgz';
  31988. }
  31989. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  31990. $err = $this->_fromUrl($dep['uri'] . $ext);
  31991. PEAR::popErrorHandling();
  31992. if (PEAR::isError($err)) {
  31993. if (!isset($options['soft'])) {
  31994. $this->_downloader->log(0, $err->getMessage());
  31995. }
  31996. return PEAR::raiseError('Invalid uri dependency "' . $dep['uri'] . $ext . '", ' .
  31997. 'cannot download');
  31998. }
  31999. } else {
  32000. $this->_parsedname =
  32001. array(
  32002. 'package' => $dep['info']->getPackage(),
  32003. 'channel' => $dep['info']->getChannel(),
  32004. 'version' => $dep['version']
  32005. );
  32006. if (!isset($dep['nodefault'])) {
  32007. $this->_parsedname['group'] = 'default'; // download the default dependency group
  32008. $this->_explicitGroup = false;
  32009. }
  32010. $this->_rawpackagefile = $dep['raw'];
  32011. }
  32012. }
  32013. function detectDependencies($params)
  32014. {
  32015. $options = $this->_downloader->getOptions();
  32016. if (isset($options['downloadonly'])) {
  32017. return;
  32018. }
  32019. if (isset($options['offline'])) {
  32020. $this->_downloader->log(3, 'Skipping dependency download check, --offline specified');
  32021. return;
  32022. }
  32023. $pname = $this->getParsedPackage();
  32024. if (!$pname) {
  32025. return;
  32026. }
  32027. $deps = $this->getDeps();
  32028. if (!$deps) {
  32029. return;
  32030. }
  32031. if (isset($deps['required'])) { // package.xml 2.0
  32032. return $this->_detect2($deps, $pname, $options, $params);
  32033. }
  32034. return $this->_detect1($deps, $pname, $options, $params);
  32035. }
  32036. function setValidated()
  32037. {
  32038. $this->_validated = true;
  32039. }
  32040. function alreadyValidated()
  32041. {
  32042. return $this->_validated;
  32043. }
  32044. /**
  32045. * Remove packages to be downloaded that are already installed
  32046. * @param array of PEAR_Downloader_Package objects
  32047. */
  32048. public static function removeInstalled(&$params)
  32049. {
  32050. if (!isset($params[0])) {
  32051. return;
  32052. }
  32053. $options = $params[0]->_downloader->getOptions();
  32054. if (!isset($options['downloadonly'])) {
  32055. foreach ($params as $i => $param) {
  32056. $package = $param->getPackage();
  32057. $channel = $param->getChannel();
  32058. // remove self if already installed with this version
  32059. // this does not need any pecl magic - we only remove exact matches
  32060. if ($param->_installRegistry->packageExists($package, $channel)) {
  32061. $packageVersion = $param->_installRegistry->packageInfo($package, 'version', $channel);
  32062. if (version_compare($packageVersion, $param->getVersion(), '==')) {
  32063. if (!isset($options['force']) && !isset($options['packagingroot'])) {
  32064. $info = $param->getParsedPackage();
  32065. unset($info['version']);
  32066. unset($info['state']);
  32067. if (!isset($options['soft'])) {
  32068. $param->_downloader->log(1, 'Skipping package "' .
  32069. $param->getShortName() .
  32070. '", already installed as version ' . $packageVersion);
  32071. }
  32072. $params[$i] = false;
  32073. }
  32074. } elseif (!isset($options['force']) && !isset($options['upgrade']) &&
  32075. !isset($options['soft']) && !isset($options['packagingroot'])) {
  32076. $info = $param->getParsedPackage();
  32077. $param->_downloader->log(1, 'Skipping package "' .
  32078. $param->getShortName() .
  32079. '", already installed as version ' . $packageVersion);
  32080. $params[$i] = false;
  32081. }
  32082. }
  32083. }
  32084. }
  32085. PEAR_Downloader_Package::removeDuplicates($params);
  32086. }
  32087. function _detect2($deps, $pname, $options, $params)
  32088. {
  32089. $this->_downloadDeps = array();
  32090. $groupnotfound = false;
  32091. foreach (array('package', 'subpackage') as $packagetype) {
  32092. // get required dependency group
  32093. if (isset($deps['required'][$packagetype])) {
  32094. if (isset($deps['required'][$packagetype][0])) {
  32095. foreach ($deps['required'][$packagetype] as $dep) {
  32096. if (isset($dep['conflicts'])) {
  32097. // skip any package that this package conflicts with
  32098. continue;
  32099. }
  32100. $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
  32101. if (is_array($ret)) {
  32102. $this->_downloadDeps[] = $ret;
  32103. } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
  32104. $this->_downloader->log(0, $ret->getMessage());
  32105. }
  32106. }
  32107. } else {
  32108. $dep = $deps['required'][$packagetype];
  32109. if (!isset($dep['conflicts'])) {
  32110. // skip any package that this package conflicts with
  32111. $ret = $this->_detect2Dep($dep, $pname, 'required', $params);
  32112. if (is_array($ret)) {
  32113. $this->_downloadDeps[] = $ret;
  32114. } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
  32115. $this->_downloader->log(0, $ret->getMessage());
  32116. }
  32117. }
  32118. }
  32119. }
  32120. // get optional dependency group, if any
  32121. if (isset($deps['optional'][$packagetype])) {
  32122. $skipnames = array();
  32123. if (!isset($deps['optional'][$packagetype][0])) {
  32124. $deps['optional'][$packagetype] = array($deps['optional'][$packagetype]);
  32125. }
  32126. foreach ($deps['optional'][$packagetype] as $dep) {
  32127. $skip = false;
  32128. if (!isset($options['alldeps'])) {
  32129. $dep['package'] = $dep['name'];
  32130. if (!isset($options['soft'])) {
  32131. $this->_downloader->log(3, 'Notice: package "' .
  32132. $this->_registry->parsedPackageNameToString($this->getParsedPackage(),
  32133. true) . '" optional dependency "' .
  32134. $this->_registry->parsedPackageNameToString(array('package' =>
  32135. $dep['name'], 'channel' => 'pear.php.net'), true) .
  32136. '" will not be automatically downloaded');
  32137. }
  32138. $skipnames[] = $this->_registry->parsedPackageNameToString($dep, true);
  32139. $skip = true;
  32140. unset($dep['package']);
  32141. }
  32142. $ret = $this->_detect2Dep($dep, $pname, 'optional', $params);
  32143. if (PEAR::isError($ret) && !isset($options['soft'])) {
  32144. $this->_downloader->log(0, $ret->getMessage());
  32145. }
  32146. if (!$ret) {
  32147. $dep['package'] = $dep['name'];
  32148. $skip = count($skipnames) ?
  32149. $skipnames[count($skipnames) - 1] : '';
  32150. if ($skip ==
  32151. $this->_registry->parsedPackageNameToString($dep, true)) {
  32152. array_pop($skipnames);
  32153. }
  32154. }
  32155. if (!$skip && is_array($ret)) {
  32156. $this->_downloadDeps[] = $ret;
  32157. }
  32158. }
  32159. if (count($skipnames)) {
  32160. if (!isset($options['soft'])) {
  32161. $this->_downloader->log(1, 'Did not download optional dependencies: ' .
  32162. implode(', ', $skipnames) .
  32163. ', use --alldeps to download automatically');
  32164. }
  32165. }
  32166. }
  32167. // get requested dependency group, if any
  32168. $groupname = $this->getGroup();
  32169. $explicit = $this->_explicitGroup;
  32170. if (!$groupname) {
  32171. if (!$this->canDefault()) {
  32172. continue;
  32173. }
  32174. $groupname = 'default'; // try the default dependency group
  32175. }
  32176. if ($groupnotfound) {
  32177. continue;
  32178. }
  32179. if (isset($deps['group'])) {
  32180. if (isset($deps['group']['attribs'])) {
  32181. if (strtolower($deps['group']['attribs']['name']) == strtolower($groupname)) {
  32182. $group = $deps['group'];
  32183. } elseif ($explicit) {
  32184. if (!isset($options['soft'])) {
  32185. $this->_downloader->log(0, 'Warning: package "' .
  32186. $this->_registry->parsedPackageNameToString($pname, true) .
  32187. '" has no dependency ' . 'group named "' . $groupname . '"');
  32188. }
  32189. $groupnotfound = true;
  32190. continue;
  32191. }
  32192. } else {
  32193. $found = false;
  32194. foreach ($deps['group'] as $group) {
  32195. if (strtolower($group['attribs']['name']) == strtolower($groupname)) {
  32196. $found = true;
  32197. break;
  32198. }
  32199. }
  32200. if (!$found) {
  32201. if ($explicit) {
  32202. if (!isset($options['soft'])) {
  32203. $this->_downloader->log(0, 'Warning: package "' .
  32204. $this->_registry->parsedPackageNameToString($pname, true) .
  32205. '" has no dependency ' . 'group named "' . $groupname . '"');
  32206. }
  32207. }
  32208. $groupnotfound = true;
  32209. continue;
  32210. }
  32211. }
  32212. }
  32213. if (isset($group) && isset($group[$packagetype])) {
  32214. if (isset($group[$packagetype][0])) {
  32215. foreach ($group[$packagetype] as $dep) {
  32216. $ret = $this->_detect2Dep($dep, $pname, 'dependency group "' .
  32217. $group['attribs']['name'] . '"', $params);
  32218. if (is_array($ret)) {
  32219. $this->_downloadDeps[] = $ret;
  32220. } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
  32221. $this->_downloader->log(0, $ret->getMessage());
  32222. }
  32223. }
  32224. } else {
  32225. $ret = $this->_detect2Dep($group[$packagetype], $pname,
  32226. 'dependency group "' .
  32227. $group['attribs']['name'] . '"', $params);
  32228. if (is_array($ret)) {
  32229. $this->_downloadDeps[] = $ret;
  32230. } elseif (PEAR::isError($ret) && !isset($options['soft'])) {
  32231. $this->_downloader->log(0, $ret->getMessage());
  32232. }
  32233. }
  32234. }
  32235. }
  32236. }
  32237. function _detect2Dep($dep, $pname, $group, $params)
  32238. {
  32239. if (isset($dep['conflicts'])) {
  32240. return true;
  32241. }
  32242. $options = $this->_downloader->getOptions();
  32243. if (isset($dep['uri'])) {
  32244. return array('uri' => $dep['uri'], 'dep' => $dep);;
  32245. }
  32246. $testdep = $dep;
  32247. $testdep['package'] = $dep['name'];
  32248. if (PEAR_Downloader_Package::willDownload($testdep, $params)) {
  32249. $dep['package'] = $dep['name'];
  32250. if (!isset($options['soft'])) {
  32251. $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group .
  32252. ' dependency "' .
  32253. $this->_registry->parsedPackageNameToString($dep, true) .
  32254. '", will be installed');
  32255. }
  32256. return false;
  32257. }
  32258. $options = $this->_downloader->getOptions();
  32259. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  32260. if ($this->_explicitState) {
  32261. $pname['state'] = $this->_explicitState;
  32262. }
  32263. $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
  32264. if (PEAR::isError($url)) {
  32265. PEAR::popErrorHandling();
  32266. return $url;
  32267. }
  32268. $dep['package'] = $dep['name'];
  32269. $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, $group == 'optional' &&
  32270. !isset($options['alldeps']), true);
  32271. PEAR::popErrorHandling();
  32272. if (PEAR::isError($ret)) {
  32273. if (!isset($options['soft'])) {
  32274. $this->_downloader->log(0, $ret->getMessage());
  32275. }
  32276. return false;
  32277. }
  32278. // check to see if a dep is already installed and is the same or newer
  32279. if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended'])) {
  32280. $oper = 'has';
  32281. } else {
  32282. $oper = 'gt';
  32283. }
  32284. // do not try to move this before getDepPackageDownloadURL
  32285. // we can't determine whether upgrade is necessary until we know what
  32286. // version would be downloaded
  32287. if (!isset($options['force']) && $this->isInstalled($ret, $oper)) {
  32288. $version = $this->_installRegistry->packageInfo($dep['name'], 'version', $dep['channel']);
  32289. $dep['package'] = $dep['name'];
  32290. if (!isset($options['soft'])) {
  32291. $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
  32292. ' dependency "' .
  32293. $this->_registry->parsedPackageNameToString($dep, true) .
  32294. '" version ' . $url['version'] . ', already installed as version ' .
  32295. $version);
  32296. }
  32297. return false;
  32298. }
  32299. if (isset($dep['nodefault'])) {
  32300. $ret['nodefault'] = true;
  32301. }
  32302. return $ret;
  32303. }
  32304. function _detect1($deps, $pname, $options, $params)
  32305. {
  32306. $this->_downloadDeps = array();
  32307. $skipnames = array();
  32308. foreach ($deps as $dep) {
  32309. $nodownload = false;
  32310. if (isset ($dep['type']) && $dep['type'] === 'pkg') {
  32311. $dep['channel'] = 'pear.php.net';
  32312. $dep['package'] = $dep['name'];
  32313. switch ($dep['rel']) {
  32314. case 'not' :
  32315. continue 2;
  32316. case 'ge' :
  32317. case 'eq' :
  32318. case 'gt' :
  32319. case 'has' :
  32320. $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
  32321. 'required' :
  32322. 'optional';
  32323. if (PEAR_Downloader_Package::willDownload($dep, $params)) {
  32324. $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
  32325. . ' dependency "' .
  32326. $this->_registry->parsedPackageNameToString($dep, true) .
  32327. '", will be installed');
  32328. continue 2;
  32329. }
  32330. $fakedp = new PEAR_PackageFile_v1;
  32331. $fakedp->setPackage($dep['name']);
  32332. // skip internet check if we are not upgrading (bug #5810)
  32333. if (!isset($options['upgrade']) && $this->isInstalled(
  32334. $fakedp, $dep['rel'])) {
  32335. $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group
  32336. . ' dependency "' .
  32337. $this->_registry->parsedPackageNameToString($dep, true) .
  32338. '", is already installed');
  32339. continue 2;
  32340. }
  32341. }
  32342. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  32343. if ($this->_explicitState) {
  32344. $pname['state'] = $this->_explicitState;
  32345. }
  32346. $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname);
  32347. $chan = 'pear.php.net';
  32348. if (PEAR::isError($url)) {
  32349. // check to see if this is a pecl package that has jumped
  32350. // from pear.php.net to pecl.php.net channel
  32351. if (!class_exists('PEAR_Dependency2')) {
  32352. require_once 'PEAR/Dependency2.php';
  32353. }
  32354. $newdep = PEAR_Dependency2::normalizeDep($dep);
  32355. $newdep = $newdep[0];
  32356. $newdep['channel'] = 'pecl.php.net';
  32357. $chan = 'pecl.php.net';
  32358. $url = $this->_downloader->_getDepPackageDownloadUrl($newdep, $pname);
  32359. $obj = &$this->_installRegistry->getPackage($dep['name']);
  32360. if (PEAR::isError($url)) {
  32361. PEAR::popErrorHandling();
  32362. if ($obj !== null && $this->isInstalled($obj, $dep['rel'])) {
  32363. $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
  32364. 'required' :
  32365. 'optional';
  32366. $dep['package'] = $dep['name'];
  32367. if (!isset($options['soft'])) {
  32368. $this->_downloader->log(3, $this->getShortName() .
  32369. ': Skipping ' . $group . ' dependency "' .
  32370. $this->_registry->parsedPackageNameToString($dep, true) .
  32371. '", already installed as version ' . $obj->getVersion());
  32372. }
  32373. $skip = count($skipnames) ?
  32374. $skipnames[count($skipnames) - 1] : '';
  32375. if ($skip ==
  32376. $this->_registry->parsedPackageNameToString($dep, true)) {
  32377. array_pop($skipnames);
  32378. }
  32379. continue;
  32380. } else {
  32381. if (isset($dep['optional']) && $dep['optional'] == 'yes') {
  32382. $this->_downloader->log(2, $this->getShortName() .
  32383. ': Skipping optional dependency "' .
  32384. $this->_registry->parsedPackageNameToString($dep, true) .
  32385. '", no releases exist');
  32386. continue;
  32387. } else {
  32388. return $url;
  32389. }
  32390. }
  32391. }
  32392. }
  32393. PEAR::popErrorHandling();
  32394. if (!isset($options['alldeps'])) {
  32395. if (isset($dep['optional']) && $dep['optional'] == 'yes') {
  32396. if (!isset($options['soft'])) {
  32397. $this->_downloader->log(3, 'Notice: package "' .
  32398. $this->getShortName() .
  32399. '" optional dependency "' .
  32400. $this->_registry->parsedPackageNameToString(
  32401. array('channel' => $chan, 'package' =>
  32402. $dep['name']), true) .
  32403. '" will not be automatically downloaded');
  32404. }
  32405. $skipnames[] = $this->_registry->parsedPackageNameToString(
  32406. array('channel' => $chan, 'package' =>
  32407. $dep['name']), true);
  32408. $nodownload = true;
  32409. }
  32410. }
  32411. if (!isset($options['alldeps']) && !isset($options['onlyreqdeps'])) {
  32412. if (!isset($dep['optional']) || $dep['optional'] == 'no') {
  32413. if (!isset($options['soft'])) {
  32414. $this->_downloader->log(3, 'Notice: package "' .
  32415. $this->getShortName() .
  32416. '" required dependency "' .
  32417. $this->_registry->parsedPackageNameToString(
  32418. array('channel' => $chan, 'package' =>
  32419. $dep['name']), true) .
  32420. '" will not be automatically downloaded');
  32421. }
  32422. $skipnames[] = $this->_registry->parsedPackageNameToString(
  32423. array('channel' => $chan, 'package' =>
  32424. $dep['name']), true);
  32425. $nodownload = true;
  32426. }
  32427. }
  32428. // check to see if a dep is already installed
  32429. // do not try to move this before getDepPackageDownloadURL
  32430. // we can't determine whether upgrade is necessary until we know what
  32431. // version would be downloaded
  32432. if (!isset($options['force']) && $this->isInstalled(
  32433. $url, $dep['rel'])) {
  32434. $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ?
  32435. 'required' :
  32436. 'optional';
  32437. $dep['package'] = $dep['name'];
  32438. if (isset($newdep)) {
  32439. $version = $this->_installRegistry->packageInfo($newdep['name'], 'version', $newdep['channel']);
  32440. } else {
  32441. $version = $this->_installRegistry->packageInfo($dep['name'], 'version');
  32442. }
  32443. $dep['version'] = $url['version'];
  32444. if (!isset($options['soft'])) {
  32445. $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group .
  32446. ' dependency "' .
  32447. $this->_registry->parsedPackageNameToString($dep, true) .
  32448. '", already installed as version ' . $version);
  32449. }
  32450. $skip = count($skipnames) ?
  32451. $skipnames[count($skipnames) - 1] : '';
  32452. if ($skip ==
  32453. $this->_registry->parsedPackageNameToString($dep, true)) {
  32454. array_pop($skipnames);
  32455. }
  32456. continue;
  32457. }
  32458. if ($nodownload) {
  32459. continue;
  32460. }
  32461. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  32462. if (isset($newdep)) {
  32463. $dep = $newdep;
  32464. }
  32465. $dep['package'] = $dep['name'];
  32466. $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params,
  32467. isset($dep['optional']) && $dep['optional'] == 'yes' &&
  32468. !isset($options['alldeps']), true);
  32469. PEAR::popErrorHandling();
  32470. if (PEAR::isError($ret)) {
  32471. if (!isset($options['soft'])) {
  32472. $this->_downloader->log(0, $ret->getMessage());
  32473. }
  32474. continue;
  32475. }
  32476. $this->_downloadDeps[] = $ret;
  32477. }
  32478. }
  32479. if (count($skipnames)) {
  32480. if (!isset($options['soft'])) {
  32481. $this->_downloader->log(1, 'Did not download dependencies: ' .
  32482. implode(', ', $skipnames) .
  32483. ', use --alldeps or --onlyreqdeps to download automatically');
  32484. }
  32485. }
  32486. }
  32487. function setDownloadURL($pkg)
  32488. {
  32489. $this->_downloadURL = $pkg;
  32490. }
  32491. /**
  32492. * Set the package.xml object for this downloaded package
  32493. *
  32494. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 $pkg
  32495. */
  32496. function setPackageFile(&$pkg)
  32497. {
  32498. $this->_packagefile = &$pkg;
  32499. }
  32500. function getShortName()
  32501. {
  32502. return $this->_registry->parsedPackageNameToString(array('channel' => $this->getChannel(),
  32503. 'package' => $this->getPackage()), true);
  32504. }
  32505. function getParsedPackage()
  32506. {
  32507. if (isset($this->_packagefile) || isset($this->_parsedname)) {
  32508. return array('channel' => $this->getChannel(),
  32509. 'package' => $this->getPackage(),
  32510. 'version' => $this->getVersion());
  32511. }
  32512. return false;
  32513. }
  32514. function getDownloadURL()
  32515. {
  32516. return $this->_downloadURL;
  32517. }
  32518. function canDefault()
  32519. {
  32520. if (isset($this->_downloadURL) && isset($this->_downloadURL['nodefault'])) {
  32521. return false;
  32522. }
  32523. return true;
  32524. }
  32525. function getPackage()
  32526. {
  32527. if (isset($this->_packagefile)) {
  32528. return $this->_packagefile->getPackage();
  32529. } elseif (isset($this->_downloadURL['info'])) {
  32530. return $this->_downloadURL['info']->getPackage();
  32531. }
  32532. return false;
  32533. }
  32534. /**
  32535. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  32536. */
  32537. function isSubpackage(&$pf)
  32538. {
  32539. if (isset($this->_packagefile)) {
  32540. return $this->_packagefile->isSubpackage($pf);
  32541. } elseif (isset($this->_downloadURL['info'])) {
  32542. return $this->_downloadURL['info']->isSubpackage($pf);
  32543. }
  32544. return false;
  32545. }
  32546. function getPackageType()
  32547. {
  32548. if (isset($this->_packagefile)) {
  32549. return $this->_packagefile->getPackageType();
  32550. } elseif (isset($this->_downloadURL['info'])) {
  32551. return $this->_downloadURL['info']->getPackageType();
  32552. }
  32553. return false;
  32554. }
  32555. function isBundle()
  32556. {
  32557. if (isset($this->_packagefile)) {
  32558. return $this->_packagefile->getPackageType() == 'bundle';
  32559. }
  32560. return false;
  32561. }
  32562. function getPackageXmlVersion()
  32563. {
  32564. if (isset($this->_packagefile)) {
  32565. return $this->_packagefile->getPackagexmlVersion();
  32566. } elseif (isset($this->_downloadURL['info'])) {
  32567. return $this->_downloadURL['info']->getPackagexmlVersion();
  32568. }
  32569. return '1.0';
  32570. }
  32571. function getChannel()
  32572. {
  32573. if (isset($this->_packagefile)) {
  32574. return $this->_packagefile->getChannel();
  32575. } elseif (isset($this->_downloadURL['info'])) {
  32576. return $this->_downloadURL['info']->getChannel();
  32577. }
  32578. return false;
  32579. }
  32580. function getURI()
  32581. {
  32582. if (isset($this->_packagefile)) {
  32583. return $this->_packagefile->getURI();
  32584. } elseif (isset($this->_downloadURL['info'])) {
  32585. return $this->_downloadURL['info']->getURI();
  32586. }
  32587. return false;
  32588. }
  32589. function getVersion()
  32590. {
  32591. if (isset($this->_packagefile)) {
  32592. return $this->_packagefile->getVersion();
  32593. } elseif (isset($this->_downloadURL['version'])) {
  32594. return $this->_downloadURL['version'];
  32595. }
  32596. return false;
  32597. }
  32598. function isCompatible($pf)
  32599. {
  32600. if (isset($this->_packagefile)) {
  32601. return $this->_packagefile->isCompatible($pf);
  32602. } elseif (isset($this->_downloadURL['info'])) {
  32603. return $this->_downloadURL['info']->isCompatible($pf);
  32604. }
  32605. return true;
  32606. }
  32607. function setGroup($group)
  32608. {
  32609. $this->_parsedname['group'] = $group;
  32610. }
  32611. function getGroup()
  32612. {
  32613. if (isset($this->_parsedname['group'])) {
  32614. return $this->_parsedname['group'];
  32615. }
  32616. return '';
  32617. }
  32618. function isExtension($name)
  32619. {
  32620. if (isset($this->_packagefile)) {
  32621. return $this->_packagefile->isExtension($name);
  32622. } elseif (isset($this->_downloadURL['info'])) {
  32623. if ($this->_downloadURL['info']->getPackagexmlVersion() == '2.0') {
  32624. return $this->_downloadURL['info']->getProvidesExtension() == $name;
  32625. }
  32626. return false;
  32627. }
  32628. return false;
  32629. }
  32630. function getDeps()
  32631. {
  32632. if (isset($this->_packagefile)) {
  32633. $ver = $this->_packagefile->getPackagexmlVersion();
  32634. if (version_compare($ver, '2.0', '>=')) {
  32635. return $this->_packagefile->getDeps(true);
  32636. }
  32637. return $this->_packagefile->getDeps();
  32638. } elseif (isset($this->_downloadURL['info'])) {
  32639. $ver = $this->_downloadURL['info']->getPackagexmlVersion();
  32640. if (version_compare($ver, '2.0', '>=')) {
  32641. return $this->_downloadURL['info']->getDeps(true);
  32642. }
  32643. return $this->_downloadURL['info']->getDeps();
  32644. }
  32645. return array();
  32646. }
  32647. /**
  32648. * @param array Parsed array from {@link PEAR_Registry::parsePackageName()} or a dependency
  32649. * returned from getDepDownloadURL()
  32650. */
  32651. function isEqual($param)
  32652. {
  32653. if (is_object($param)) {
  32654. $channel = $param->getChannel();
  32655. $package = $param->getPackage();
  32656. if ($param->getURI()) {
  32657. $param = array(
  32658. 'channel' => $param->getChannel(),
  32659. 'package' => $param->getPackage(),
  32660. 'version' => $param->getVersion(),
  32661. 'uri' => $param->getURI(),
  32662. );
  32663. } else {
  32664. $param = array(
  32665. 'channel' => $param->getChannel(),
  32666. 'package' => $param->getPackage(),
  32667. 'version' => $param->getVersion(),
  32668. );
  32669. }
  32670. } else {
  32671. if (isset($param['uri'])) {
  32672. if ($this->getChannel() != '__uri') {
  32673. return false;
  32674. }
  32675. return $param['uri'] == $this->getURI();
  32676. }
  32677. $package = isset($param['package']) ? $param['package'] : $param['info']->getPackage();
  32678. $channel = isset($param['channel']) ? $param['channel'] : $param['info']->getChannel();
  32679. if (isset($param['rel'])) {
  32680. if (!class_exists('PEAR_Dependency2')) {
  32681. require_once 'PEAR/Dependency2.php';
  32682. }
  32683. $newdep = PEAR_Dependency2::normalizeDep($param);
  32684. $newdep = $newdep[0];
  32685. } elseif (isset($param['min'])) {
  32686. $newdep = $param;
  32687. }
  32688. }
  32689. if (isset($newdep)) {
  32690. if (!isset($newdep['min'])) {
  32691. $newdep['min'] = '0';
  32692. }
  32693. if (!isset($newdep['max'])) {
  32694. $newdep['max'] = '100000000000000000000';
  32695. }
  32696. // use magic to support pecl packages suddenly jumping to the pecl channel
  32697. // we need to support both dependency possibilities
  32698. if ($channel == 'pear.php.net' && $this->getChannel() == 'pecl.php.net') {
  32699. if ($package == $this->getPackage()) {
  32700. $channel = 'pecl.php.net';
  32701. }
  32702. }
  32703. if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
  32704. if ($package == $this->getPackage()) {
  32705. $channel = 'pear.php.net';
  32706. }
  32707. }
  32708. return (strtolower($package) == strtolower($this->getPackage()) &&
  32709. $channel == $this->getChannel() &&
  32710. version_compare($newdep['min'], $this->getVersion(), '<=') &&
  32711. version_compare($newdep['max'], $this->getVersion(), '>='));
  32712. }
  32713. // use magic to support pecl packages suddenly jumping to the pecl channel
  32714. if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') {
  32715. if (strtolower($package) == strtolower($this->getPackage())) {
  32716. $channel = 'pear.php.net';
  32717. }
  32718. }
  32719. if (isset($param['version'])) {
  32720. return (strtolower($package) == strtolower($this->getPackage()) &&
  32721. $channel == $this->getChannel() &&
  32722. $param['version'] == $this->getVersion());
  32723. }
  32724. return strtolower($package) == strtolower($this->getPackage()) &&
  32725. $channel == $this->getChannel();
  32726. }
  32727. function isInstalled($dep, $oper = '==')
  32728. {
  32729. if (!$dep) {
  32730. return false;
  32731. }
  32732. if ($oper != 'ge' && $oper != 'gt' && $oper != 'has' && $oper != '==') {
  32733. return false;
  32734. }
  32735. if (is_object($dep)) {
  32736. $package = $dep->getPackage();
  32737. $channel = $dep->getChannel();
  32738. if ($dep->getURI()) {
  32739. $dep = array(
  32740. 'uri' => $dep->getURI(),
  32741. 'version' => $dep->getVersion(),
  32742. );
  32743. } else {
  32744. $dep = array(
  32745. 'version' => $dep->getVersion(),
  32746. );
  32747. }
  32748. } else {
  32749. if (isset($dep['uri'])) {
  32750. $channel = '__uri';
  32751. $package = $dep['dep']['name'];
  32752. } else {
  32753. $channel = $dep['info']->getChannel();
  32754. $package = $dep['info']->getPackage();
  32755. }
  32756. }
  32757. $options = $this->_downloader->getOptions();
  32758. $test = $this->_installRegistry->packageExists($package, $channel);
  32759. if (!$test && $channel == 'pecl.php.net') {
  32760. // do magic to allow upgrading from old pecl packages to new ones
  32761. $test = $this->_installRegistry->packageExists($package, 'pear.php.net');
  32762. $channel = 'pear.php.net';
  32763. }
  32764. if ($test) {
  32765. if (isset($dep['uri'])) {
  32766. if ($this->_installRegistry->packageInfo($package, 'uri', '__uri') == $dep['uri']) {
  32767. return true;
  32768. }
  32769. }
  32770. if (isset($options['upgrade'])) {
  32771. $packageVersion = $this->_installRegistry->packageInfo($package, 'version', $channel);
  32772. if (version_compare($packageVersion, $dep['version'], '>=')) {
  32773. return true;
  32774. }
  32775. return false;
  32776. }
  32777. return true;
  32778. }
  32779. return false;
  32780. }
  32781. /**
  32782. * Detect duplicate package names with differing versions
  32783. *
  32784. * If a user requests to install Date 1.4.6 and Date 1.4.7,
  32785. * for instance, this is a logic error. This method
  32786. * detects this situation.
  32787. *
  32788. * @param array $params array of PEAR_Downloader_Package objects
  32789. * @param array $errorparams empty array
  32790. * @return array array of stupid duplicated packages in PEAR_Downloader_Package obejcts
  32791. */
  32792. public static function detectStupidDuplicates($params, &$errorparams)
  32793. {
  32794. $existing = array();
  32795. foreach ($params as $i => $param) {
  32796. $package = $param->getPackage();
  32797. $channel = $param->getChannel();
  32798. $group = $param->getGroup();
  32799. if (!isset($existing[$channel . '/' . $package])) {
  32800. $existing[$channel . '/' . $package] = array();
  32801. }
  32802. if (!isset($existing[$channel . '/' . $package][$group])) {
  32803. $existing[$channel . '/' . $package][$group] = array();
  32804. }
  32805. $existing[$channel . '/' . $package][$group][] = $i;
  32806. }
  32807. $indices = array();
  32808. foreach ($existing as $package => $groups) {
  32809. foreach ($groups as $group => $dupes) {
  32810. if (count($dupes) > 1) {
  32811. $indices = $indices + $dupes;
  32812. }
  32813. }
  32814. }
  32815. $indices = array_unique($indices);
  32816. foreach ($indices as $index) {
  32817. $errorparams[] = $params[$index];
  32818. }
  32819. return count($errorparams);
  32820. }
  32821. /**
  32822. * @param array
  32823. * @param bool ignore install groups - for final removal of dupe packages
  32824. */
  32825. public static function removeDuplicates(&$params, $ignoreGroups = false)
  32826. {
  32827. $pnames = array();
  32828. foreach ($params as $i => $param) {
  32829. if (!$param) {
  32830. continue;
  32831. }
  32832. if ($param->getPackage()) {
  32833. $group = $ignoreGroups ? '' : $param->getGroup();
  32834. $pnames[$i] = $param->getChannel() . '/' .
  32835. $param->getPackage() . '-' . $param->getVersion() . '#' . $group;
  32836. }
  32837. }
  32838. $pnames = array_unique($pnames);
  32839. $unset = array_diff(array_keys($params), array_keys($pnames));
  32840. $testp = array_flip($pnames);
  32841. foreach ($params as $i => $param) {
  32842. if (!$param) {
  32843. $unset[] = $i;
  32844. continue;
  32845. }
  32846. if (!is_a($param, 'PEAR_Downloader_Package')) {
  32847. $unset[] = $i;
  32848. continue;
  32849. }
  32850. $group = $ignoreGroups ? '' : $param->getGroup();
  32851. if (!isset($testp[$param->getChannel() . '/' . $param->getPackage() . '-' .
  32852. $param->getVersion() . '#' . $group])) {
  32853. $unset[] = $i;
  32854. }
  32855. }
  32856. foreach ($unset as $i) {
  32857. unset($params[$i]);
  32858. }
  32859. $ret = array();
  32860. foreach ($params as $i => $param) {
  32861. $ret[] = &$params[$i];
  32862. }
  32863. $params = array();
  32864. foreach ($ret as $i => $param) {
  32865. $params[] = &$ret[$i];
  32866. }
  32867. }
  32868. function explicitState()
  32869. {
  32870. return $this->_explicitState;
  32871. }
  32872. function setExplicitState($s)
  32873. {
  32874. $this->_explicitState = $s;
  32875. }
  32876. /**
  32877. */
  32878. public static function mergeDependencies(&$params)
  32879. {
  32880. $bundles = $newparams = array();
  32881. foreach ($params as $i => $param) {
  32882. if (!$param->isBundle()) {
  32883. continue;
  32884. }
  32885. $bundles[] = $i;
  32886. $pf = &$param->getPackageFile();
  32887. $newdeps = array();
  32888. $contents = $pf->getBundledPackages();
  32889. if (!is_array($contents)) {
  32890. $contents = array($contents);
  32891. }
  32892. foreach ($contents as $file) {
  32893. $filecontents = $pf->getFileContents($file);
  32894. $dl = &$param->getDownloader();
  32895. $options = $dl->getOptions();
  32896. if (PEAR::isError($dir = $dl->getDownloadDir())) {
  32897. return $dir;
  32898. }
  32899. $fp = @fopen($dir . DIRECTORY_SEPARATOR . $file, 'wb');
  32900. if (!$fp) {
  32901. continue;
  32902. }
  32903. // FIXME do symlink check
  32904. fwrite($fp, $filecontents, strlen($filecontents));
  32905. fclose($fp);
  32906. if ($s = $params[$i]->explicitState()) {
  32907. $obj->setExplicitState($s);
  32908. }
  32909. $obj = new PEAR_Downloader_Package($params[$i]->getDownloader());
  32910. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  32911. if (PEAR::isError($dir = $dl->getDownloadDir())) {
  32912. PEAR::popErrorHandling();
  32913. return $dir;
  32914. }
  32915. $a = $dir . DIRECTORY_SEPARATOR . $file;
  32916. $e = $obj->_fromFile($a);
  32917. PEAR::popErrorHandling();
  32918. if (PEAR::isError($e)) {
  32919. if (!isset($options['soft'])) {
  32920. $dl->log(0, $e->getMessage());
  32921. }
  32922. continue;
  32923. }
  32924. if (!PEAR_Downloader_Package::willDownload($obj,
  32925. array_merge($params, $newparams)) && !$param->isInstalled($obj)) {
  32926. $newparams[] = $obj;
  32927. }
  32928. }
  32929. }
  32930. foreach ($bundles as $i) {
  32931. unset($params[$i]); // remove bundles - only their contents matter for installation
  32932. }
  32933. PEAR_Downloader_Package::removeDuplicates($params); // strip any unset indices
  32934. if (count($newparams)) { // add in bundled packages for install
  32935. foreach ($newparams as $i => $unused) {
  32936. $params[] = &$newparams[$i];
  32937. }
  32938. $newparams = array();
  32939. }
  32940. foreach ($params as $i => $param) {
  32941. $newdeps = array();
  32942. foreach ($param->_downloadDeps as $dep) {
  32943. $merge = array_merge($params, $newparams);
  32944. if (!PEAR_Downloader_Package::willDownload($dep, $merge)
  32945. && !$param->isInstalled($dep)
  32946. ) {
  32947. $newdeps[] = $dep;
  32948. } else {
  32949. //var_dump($dep);
  32950. // detect versioning conflicts here
  32951. }
  32952. }
  32953. // convert the dependencies into PEAR_Downloader_Package objects for the next time around
  32954. $params[$i]->_downloadDeps = array();
  32955. foreach ($newdeps as $dep) {
  32956. $obj = new PEAR_Downloader_Package($params[$i]->getDownloader());
  32957. if ($s = $params[$i]->explicitState()) {
  32958. $obj->setExplicitState($s);
  32959. }
  32960. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  32961. $e = $obj->fromDepURL($dep);
  32962. PEAR::popErrorHandling();
  32963. if (PEAR::isError($e)) {
  32964. if (!isset($options['soft'])) {
  32965. $obj->_downloader->log(0, $e->getMessage());
  32966. }
  32967. continue;
  32968. }
  32969. $e = $obj->detectDependencies($params);
  32970. if (PEAR::isError($e)) {
  32971. if (!isset($options['soft'])) {
  32972. $obj->_downloader->log(0, $e->getMessage());
  32973. }
  32974. }
  32975. $newparams[] = $obj;
  32976. }
  32977. }
  32978. if (count($newparams)) {
  32979. foreach ($newparams as $i => $unused) {
  32980. $params[] = &$newparams[$i];
  32981. }
  32982. return true;
  32983. }
  32984. return false;
  32985. }
  32986. /**
  32987. */
  32988. public static function willDownload($param, $params)
  32989. {
  32990. if (!is_array($params)) {
  32991. return false;
  32992. }
  32993. foreach ($params as $obj) {
  32994. if ($obj->isEqual($param)) {
  32995. return true;
  32996. }
  32997. }
  32998. return false;
  32999. }
  33000. /**
  33001. * For simpler unit-testing
  33002. * @param PEAR_Config
  33003. * @param int
  33004. * @param string
  33005. */
  33006. function &getPackagefileObject(&$c, $d)
  33007. {
  33008. $a = new PEAR_PackageFile($c, $d);
  33009. return $a;
  33010. }
  33011. /**
  33012. * This will retrieve from a local file if possible, and parse out
  33013. * a group name as well. The original parameter will be modified to reflect this.
  33014. * @param string|array can be a parsed package name as well
  33015. * @access private
  33016. */
  33017. function _fromFile(&$param)
  33018. {
  33019. $saveparam = $param;
  33020. if (is_string($param) && substr($param, 0, 10) !== 'channel://') {
  33021. if (!@file_exists($param)) {
  33022. $test = explode('#', $param);
  33023. $group = array_pop($test);
  33024. if (@file_exists(implode('#', $test))) {
  33025. $this->setGroup($group);
  33026. $param = implode('#', $test);
  33027. $this->_explicitGroup = true;
  33028. }
  33029. }
  33030. if (@is_file($param)) {
  33031. $this->_type = 'local';
  33032. $options = $this->_downloader->getOptions();
  33033. $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->_debug);
  33034. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  33035. $pf = &$pkg->fromAnyFile($param, PEAR_VALIDATE_INSTALLING);
  33036. PEAR::popErrorHandling();
  33037. if (PEAR::isError($pf)) {
  33038. $this->_valid = false;
  33039. $param = $saveparam;
  33040. return $pf;
  33041. }
  33042. $this->_packagefile = &$pf;
  33043. if (!$this->getGroup()) {
  33044. $this->setGroup('default'); // install the default dependency group
  33045. }
  33046. return $this->_valid = true;
  33047. }
  33048. }
  33049. $param = $saveparam;
  33050. return $this->_valid = false;
  33051. }
  33052. function _fromUrl($param, $saveparam = '')
  33053. {
  33054. if (!is_array($param) && (preg_match('#^(http|https|ftp)://#', $param))) {
  33055. $options = $this->_downloader->getOptions();
  33056. $this->_type = 'url';
  33057. $callback = $this->_downloader->ui ?
  33058. array(&$this->_downloader, '_downloadCallback') : null;
  33059. $this->_downloader->pushErrorHandling(PEAR_ERROR_RETURN);
  33060. if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) {
  33061. $this->_downloader->popErrorHandling();
  33062. return $dir;
  33063. }
  33064. $this->_downloader->log(3, 'Downloading "' . $param . '"');
  33065. $file = $this->_downloader->downloadHttp($param, $this->_downloader->ui,
  33066. $dir, $callback, null, false, $this->getChannel());
  33067. $this->_downloader->popErrorHandling();
  33068. if (PEAR::isError($file)) {
  33069. if (!empty($saveparam)) {
  33070. $saveparam = ", cannot download \"$saveparam\"";
  33071. }
  33072. $err = PEAR::raiseError('Could not download from "' . $param .
  33073. '"' . $saveparam . ' (' . $file->getMessage() . ')');
  33074. return $err;
  33075. }
  33076. if ($this->_rawpackagefile) {
  33077. require_once 'Archive/Tar.php';
  33078. $tar = new Archive_Tar($file);
  33079. $packagexml = $tar->extractInString('package2.xml');
  33080. if (!$packagexml) {
  33081. $packagexml = $tar->extractInString('package.xml');
  33082. }
  33083. if (str_replace(array("\n", "\r"), array('',''), $packagexml) !=
  33084. str_replace(array("\n", "\r"), array('',''), $this->_rawpackagefile)) {
  33085. if ($this->getChannel() != 'pear.php.net') {
  33086. return PEAR::raiseError('CRITICAL ERROR: package.xml downloaded does ' .
  33087. 'not match value returned from xml-rpc');
  33088. }
  33089. // be more lax for the existing PEAR packages that have not-ok
  33090. // characters in their package.xml
  33091. $this->_downloader->log(0, 'CRITICAL WARNING: The "' .
  33092. $this->getPackage() . '" package has invalid characters in its ' .
  33093. 'package.xml. The next version of PEAR may not be able to install ' .
  33094. 'this package for security reasons. Please open a bug report at ' .
  33095. 'http://pear.php.net/package/' . $this->getPackage() . '/bugs');
  33096. }
  33097. }
  33098. // whew, download worked!
  33099. $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug);
  33100. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  33101. $pf = &$pkg->fromAnyFile($file, PEAR_VALIDATE_INSTALLING);
  33102. PEAR::popErrorHandling();
  33103. if (PEAR::isError($pf)) {
  33104. if (is_array($pf->getUserInfo())) {
  33105. foreach ($pf->getUserInfo() as $err) {
  33106. if (is_array($err)) {
  33107. $err = $err['message'];
  33108. }
  33109. if (!isset($options['soft'])) {
  33110. $this->_downloader->log(0, "Validation Error: $err");
  33111. }
  33112. }
  33113. }
  33114. if (!isset($options['soft'])) {
  33115. $this->_downloader->log(0, $pf->getMessage());
  33116. }
  33117. ///FIXME need to pass back some error code that we can use to match with to cancel all further operations
  33118. /// At least stop all deps of this package from being installed
  33119. $out = $saveparam ? $saveparam : $param;
  33120. $err = PEAR::raiseError('Download of "' . $out . '" succeeded, but it is not a valid package archive');
  33121. $this->_valid = false;
  33122. return $err;
  33123. }
  33124. $this->_packagefile = &$pf;
  33125. $this->setGroup('default'); // install the default dependency group
  33126. return $this->_valid = true;
  33127. }
  33128. return $this->_valid = false;
  33129. }
  33130. /**
  33131. *
  33132. * @param string|array pass in an array of format
  33133. * array(
  33134. * 'package' => 'pname',
  33135. * ['channel' => 'channame',]
  33136. * ['version' => 'version',]
  33137. * ['state' => 'state',])
  33138. * or a string of format [channame/]pname[-version|-state]
  33139. */
  33140. function _fromString($param)
  33141. {
  33142. $options = $this->_downloader->getOptions();
  33143. $channel = $this->_config->get('default_channel');
  33144. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  33145. $pname = $this->_registry->parsePackageName($param, $channel);
  33146. PEAR::popErrorHandling();
  33147. if (PEAR::isError($pname)) {
  33148. if ($pname->getCode() == 'invalid') {
  33149. $this->_valid = false;
  33150. return false;
  33151. }
  33152. if ($pname->getCode() == 'channel') {
  33153. $parsed = $pname->getUserInfo();
  33154. if ($this->_downloader->discover($parsed['channel'])) {
  33155. if ($this->_config->get('auto_discover')) {
  33156. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  33157. $pname = $this->_registry->parsePackageName($param, $channel);
  33158. PEAR::popErrorHandling();
  33159. } else {
  33160. if (!isset($options['soft'])) {
  33161. $this->_downloader->log(0, 'Channel "' . $parsed['channel'] .
  33162. '" is not initialized, use ' .
  33163. '"pear channel-discover ' . $parsed['channel'] . '" to initialize' .
  33164. 'or pear config-set auto_discover 1');
  33165. }
  33166. }
  33167. }
  33168. if (PEAR::isError($pname)) {
  33169. if (!isset($options['soft'])) {
  33170. $this->_downloader->log(0, $pname->getMessage());
  33171. }
  33172. if (is_array($param)) {
  33173. $param = $this->_registry->parsedPackageNameToString($param);
  33174. }
  33175. $err = PEAR::raiseError('invalid package name/package file "' . $param . '"');
  33176. $this->_valid = false;
  33177. return $err;
  33178. }
  33179. } else {
  33180. if (!isset($options['soft'])) {
  33181. $this->_downloader->log(0, $pname->getMessage());
  33182. }
  33183. $err = PEAR::raiseError('invalid package name/package file "' . $param . '"');
  33184. $this->_valid = false;
  33185. return $err;
  33186. }
  33187. }
  33188. if (!isset($this->_type)) {
  33189. $this->_type = 'rest';
  33190. }
  33191. $this->_parsedname = $pname;
  33192. $this->_explicitState = isset($pname['state']) ? $pname['state'] : false;
  33193. $this->_explicitGroup = isset($pname['group']) ? true : false;
  33194. $info = $this->_downloader->_getPackageDownloadUrl($pname);
  33195. if (PEAR::isError($info)) {
  33196. if ($info->getCode() != -976 && $pname['channel'] == 'pear.php.net') {
  33197. // try pecl
  33198. $pname['channel'] = 'pecl.php.net';
  33199. if ($test = $this->_downloader->_getPackageDownloadUrl($pname)) {
  33200. if (!PEAR::isError($test)) {
  33201. $info = PEAR::raiseError($info->getMessage() . ' - package ' .
  33202. $this->_registry->parsedPackageNameToString($pname, true) .
  33203. ' can be installed with "pecl install ' . $pname['package'] .
  33204. '"');
  33205. } else {
  33206. $pname['channel'] = 'pear.php.net';
  33207. }
  33208. } else {
  33209. $pname['channel'] = 'pear.php.net';
  33210. }
  33211. }
  33212. return $info;
  33213. }
  33214. $this->_rawpackagefile = $info['raw'];
  33215. $ret = $this->_analyzeDownloadURL($info, $param, $pname);
  33216. if (PEAR::isError($ret)) {
  33217. return $ret;
  33218. }
  33219. if ($ret) {
  33220. $this->_downloadURL = $ret;
  33221. return $this->_valid = (bool) $ret;
  33222. }
  33223. }
  33224. /**
  33225. * @param array output of package.getDownloadURL
  33226. * @param string|array|object information for detecting packages to be downloaded, and
  33227. * for errors
  33228. * @param array name information of the package
  33229. * @param array|null packages to be downloaded
  33230. * @param bool is this an optional dependency?
  33231. * @param bool is this any kind of dependency?
  33232. * @access private
  33233. */
  33234. function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = false,
  33235. $isdependency = false)
  33236. {
  33237. if (!is_string($param) && PEAR_Downloader_Package::willDownload($param, $params)) {
  33238. return false;
  33239. }
  33240. if ($info === false) {
  33241. $saveparam = !is_string($param) ? ", cannot download \"$param\"" : '';
  33242. // no releases exist
  33243. return PEAR::raiseError('No releases for package "' .
  33244. $this->_registry->parsedPackageNameToString($pname, true) . '" exist' . $saveparam);
  33245. }
  33246. if (strtolower($info['info']->getChannel()) != strtolower($pname['channel'])) {
  33247. $err = false;
  33248. if ($pname['channel'] == 'pecl.php.net') {
  33249. if ($info['info']->getChannel() != 'pear.php.net') {
  33250. $err = true;
  33251. }
  33252. } elseif ($info['info']->getChannel() == 'pecl.php.net') {
  33253. if ($pname['channel'] != 'pear.php.net') {
  33254. $err = true;
  33255. }
  33256. } else {
  33257. $err = true;
  33258. }
  33259. if ($err) {
  33260. return PEAR::raiseError('SECURITY ERROR: package in channel "' . $pname['channel'] .
  33261. '" retrieved another channel\'s name for download! ("' .
  33262. $info['info']->getChannel() . '")');
  33263. }
  33264. }
  33265. $preferred_state = $this->_config->get('preferred_state');
  33266. if (!isset($info['url'])) {
  33267. $package_version = $this->_registry->packageInfo($info['info']->getPackage(),
  33268. 'version', $info['info']->getChannel());
  33269. if ($this->isInstalled($info)) {
  33270. if ($isdependency && version_compare($info['version'], $package_version, '<=')) {
  33271. // ignore bogus errors of "failed to download dependency"
  33272. // if it is already installed and the one that would be
  33273. // downloaded is older or the same version (Bug #7219)
  33274. return false;
  33275. }
  33276. }
  33277. if ($info['version'] === $package_version) {
  33278. if (!isset($options['soft'])) {
  33279. $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
  33280. '/' . $pname['package'] . '-' . $package_version. ', additionally the suggested version' .
  33281. ' (' . $package_version . ') is the same as the locally installed one.');
  33282. }
  33283. return false;
  33284. }
  33285. if (version_compare($info['version'], $package_version, '<=')) {
  33286. if (!isset($options['soft'])) {
  33287. $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
  33288. '/' . $pname['package'] . '-' . $package_version . ', additionally the suggested version' .
  33289. ' (' . $info['version'] . ') is a lower version than the locally installed one (' . $package_version . ').');
  33290. }
  33291. return false;
  33292. }
  33293. $instead = ', will instead download version ' . $info['version'] .
  33294. ', stability "' . $info['info']->getState() . '"';
  33295. // releases exist, but we failed to get any
  33296. if (isset($this->_downloader->_options['force'])) {
  33297. if (isset($pname['version'])) {
  33298. $vs = ', version "' . $pname['version'] . '"';
  33299. } elseif (isset($pname['state'])) {
  33300. $vs = ', stability "' . $pname['state'] . '"';
  33301. } elseif ($param == 'dependency') {
  33302. if (!class_exists('PEAR_Common')) {
  33303. require_once 'PEAR/Common.php';
  33304. }
  33305. if (!in_array($info['info']->getState(),
  33306. PEAR_Common::betterStates($preferred_state, true))) {
  33307. if ($optional) {
  33308. // don't spit out confusing error message
  33309. return $this->_downloader->_getPackageDownloadUrl(
  33310. array('package' => $pname['package'],
  33311. 'channel' => $pname['channel'],
  33312. 'version' => $info['version']));
  33313. }
  33314. $vs = ' within preferred state "' . $preferred_state .
  33315. '"';
  33316. } else {
  33317. if (!class_exists('PEAR_Dependency2')) {
  33318. require_once 'PEAR/Dependency2.php';
  33319. }
  33320. if ($optional) {
  33321. // don't spit out confusing error message
  33322. return $this->_downloader->_getPackageDownloadUrl(
  33323. array('package' => $pname['package'],
  33324. 'channel' => $pname['channel'],
  33325. 'version' => $info['version']));
  33326. }
  33327. $vs = PEAR_Dependency2::_getExtraString($pname);
  33328. $instead = '';
  33329. }
  33330. } else {
  33331. $vs = ' within preferred state "' . $preferred_state . '"';
  33332. }
  33333. if (!isset($options['soft'])) {
  33334. $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] .
  33335. '/' . $pname['package'] . $vs . $instead);
  33336. }
  33337. // download the latest release
  33338. return $this->_downloader->_getPackageDownloadUrl(
  33339. array('package' => $pname['package'],
  33340. 'channel' => $pname['channel'],
  33341. 'version' => $info['version']));
  33342. } else {
  33343. if (isset($info['php']) && $info['php']) {
  33344. $err = PEAR::raiseError('Failed to download ' .
  33345. $this->_registry->parsedPackageNameToString(
  33346. array('channel' => $pname['channel'],
  33347. 'package' => $pname['package']),
  33348. true) .
  33349. ', latest release is version ' . $info['php']['v'] .
  33350. ', but it requires PHP version "' .
  33351. $info['php']['m'] . '", use "' .
  33352. $this->_registry->parsedPackageNameToString(
  33353. array('channel' => $pname['channel'], 'package' => $pname['package'],
  33354. 'version' => $info['php']['v'])) . '" to install',
  33355. PEAR_DOWNLOADER_PACKAGE_PHPVERSION);
  33356. return $err;
  33357. }
  33358. // construct helpful error message
  33359. if (isset($pname['version'])) {
  33360. $vs = ', version "' . $pname['version'] . '"';
  33361. } elseif (isset($pname['state'])) {
  33362. $vs = ', stability "' . $pname['state'] . '"';
  33363. } elseif ($param == 'dependency') {
  33364. if (!class_exists('PEAR_Common')) {
  33365. require_once 'PEAR/Common.php';
  33366. }
  33367. if (!in_array($info['info']->getState(),
  33368. PEAR_Common::betterStates($preferred_state, true))) {
  33369. if ($optional) {
  33370. // don't spit out confusing error message, and don't die on
  33371. // optional dep failure!
  33372. return $this->_downloader->_getPackageDownloadUrl(
  33373. array('package' => $pname['package'],
  33374. 'channel' => $pname['channel'],
  33375. 'version' => $info['version']));
  33376. }
  33377. $vs = ' within preferred state "' . $preferred_state . '"';
  33378. } else {
  33379. if (!class_exists('PEAR_Dependency2')) {
  33380. require_once 'PEAR/Dependency2.php';
  33381. }
  33382. if ($optional) {
  33383. // don't spit out confusing error message, and don't die on
  33384. // optional dep failure!
  33385. return $this->_downloader->_getPackageDownloadUrl(
  33386. array('package' => $pname['package'],
  33387. 'channel' => $pname['channel'],
  33388. 'version' => $info['version']));
  33389. }
  33390. $vs = PEAR_Dependency2::_getExtraString($pname);
  33391. }
  33392. } else {
  33393. $vs = ' within preferred state "' . $this->_downloader->config->get('preferred_state') . '"';
  33394. }
  33395. $options = $this->_downloader->getOptions();
  33396. // this is only set by the "download-all" command
  33397. if (isset($options['ignorepreferred_state'])) {
  33398. $err = PEAR::raiseError(
  33399. 'Failed to download ' . $this->_registry->parsedPackageNameToString(
  33400. array('channel' => $pname['channel'], 'package' => $pname['package']),
  33401. true)
  33402. . $vs .
  33403. ', latest release is version ' . $info['version'] .
  33404. ', stability "' . $info['info']->getState() . '", use "' .
  33405. $this->_registry->parsedPackageNameToString(
  33406. array('channel' => $pname['channel'], 'package' => $pname['package'],
  33407. 'version' => $info['version'])) . '" to install',
  33408. PEAR_DOWNLOADER_PACKAGE_STATE);
  33409. return $err;
  33410. }
  33411. // Checks if the user has a package installed already and checks the release against
  33412. // the state against the installed package, this allows upgrades for packages
  33413. // with lower stability than the preferred_state
  33414. $stability = $this->_registry->packageInfo($pname['package'], 'stability', $pname['channel']);
  33415. if (!$this->isInstalled($info)
  33416. || !in_array($info['info']->getState(), PEAR_Common::betterStates($stability['release'], true))
  33417. ) {
  33418. $err = PEAR::raiseError(
  33419. 'Failed to download ' . $this->_registry->parsedPackageNameToString(
  33420. array('channel' => $pname['channel'], 'package' => $pname['package']),
  33421. true)
  33422. . $vs .
  33423. ', latest release is version ' . $info['version'] .
  33424. ', stability "' . $info['info']->getState() . '", use "' .
  33425. $this->_registry->parsedPackageNameToString(
  33426. array('channel' => $pname['channel'], 'package' => $pname['package'],
  33427. 'version' => $info['version'])) . '" to install');
  33428. return $err;
  33429. }
  33430. }
  33431. }
  33432. if (isset($info['deprecated']) && $info['deprecated']) {
  33433. $this->_downloader->log(0,
  33434. 'WARNING: "' .
  33435. $this->_registry->parsedPackageNameToString(
  33436. array('channel' => $info['info']->getChannel(),
  33437. 'package' => $info['info']->getPackage()), true) .
  33438. '" is deprecated in favor of "' .
  33439. $this->_registry->parsedPackageNameToString($info['deprecated'], true) .
  33440. '"');
  33441. }
  33442. return $info;
  33443. }
  33444. }
  33445. ���������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Frontend/CLI.php������������������������������������������������������������������0000644�0001750�0001750�00000062115�14720722517�015721� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  33446. /**
  33447. * PEAR_Frontend_CLI
  33448. *
  33449. * PHP versions 4 and 5
  33450. *
  33451. * @category pear
  33452. * @package PEAR
  33453. * @author Stig Bakken <ssb@php.net>
  33454. * @author Greg Beaver <cellog@php.net>
  33455. * @copyright 1997-2009 The Authors
  33456. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  33457. * @link http://pear.php.net/package/PEAR
  33458. * @since File available since Release 0.1
  33459. */
  33460. /**
  33461. * base class
  33462. */
  33463. require_once 'PEAR/Frontend.php';
  33464. /**
  33465. * Command-line Frontend for the PEAR Installer
  33466. * @category pear
  33467. * @package PEAR
  33468. * @author Stig Bakken <ssb@php.net>
  33469. * @author Greg Beaver <cellog@php.net>
  33470. * @copyright 1997-2009 The Authors
  33471. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  33472. * @version Release: 1.10.16
  33473. * @link http://pear.php.net/package/PEAR
  33474. * @since Class available since Release 0.1
  33475. */
  33476. class PEAR_Frontend_CLI extends PEAR_Frontend
  33477. {
  33478. /**
  33479. * What type of user interface this frontend is for.
  33480. * @var string
  33481. * @access public
  33482. */
  33483. var $type = 'CLI';
  33484. var $lp = ''; // line prefix
  33485. var $params = array();
  33486. var $term = array(
  33487. 'bold' => '',
  33488. 'normal' => '',
  33489. );
  33490. function __construct()
  33491. {
  33492. parent::__construct();
  33493. $term = getenv('TERM'); //(cox) $_ENV is empty for me in 4.1.1
  33494. if (function_exists('posix_isatty') && !posix_isatty(1)) {
  33495. // output is being redirected to a file or through a pipe
  33496. } elseif ($term) {
  33497. if (preg_match('/^(xterm|vt220|linux)/', $term)) {
  33498. $this->term['bold'] = sprintf("%c%c%c%c", 27, 91, 49, 109);
  33499. $this->term['normal'] = sprintf("%c%c%c", 27, 91, 109);
  33500. } elseif (preg_match('/^vt100/', $term)) {
  33501. $this->term['bold'] = sprintf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0);
  33502. $this->term['normal'] = sprintf("%c%c%c%c%c", 27, 91, 109, 0, 0);
  33503. }
  33504. } elseif (OS_WINDOWS) {
  33505. // XXX add ANSI codes here
  33506. }
  33507. }
  33508. /**
  33509. * @param object PEAR_Error object
  33510. */
  33511. function displayError($e)
  33512. {
  33513. return $this->_displayLine($e->getMessage());
  33514. }
  33515. /**
  33516. * @param object PEAR_Error object
  33517. */
  33518. function displayFatalError($eobj)
  33519. {
  33520. $this->displayError($eobj);
  33521. if (class_exists('PEAR_Config')) {
  33522. $config = &PEAR_Config::singleton();
  33523. if ($config->get('verbose') > 5) {
  33524. if (function_exists('debug_print_backtrace')) {
  33525. debug_print_backtrace();
  33526. exit(1);
  33527. }
  33528. $raised = false;
  33529. foreach (debug_backtrace() as $i => $frame) {
  33530. if (!$raised) {
  33531. if (isset($frame['class'])
  33532. && strtolower($frame['class']) == 'pear'
  33533. && strtolower($frame['function']) == 'raiseerror'
  33534. ) {
  33535. $raised = true;
  33536. } else {
  33537. continue;
  33538. }
  33539. }
  33540. $frame['class'] = !isset($frame['class']) ? '' : $frame['class'];
  33541. $frame['type'] = !isset($frame['type']) ? '' : $frame['type'];
  33542. $frame['function'] = !isset($frame['function']) ? '' : $frame['function'];
  33543. $frame['line'] = !isset($frame['line']) ? '' : $frame['line'];
  33544. $this->_displayLine("#$i: $frame[class]$frame[type]$frame[function] $frame[line]");
  33545. }
  33546. }
  33547. }
  33548. exit(1);
  33549. }
  33550. /**
  33551. * Instruct the runInstallScript method to skip a paramgroup that matches the
  33552. * id value passed in.
  33553. *
  33554. * This method is useful for dynamically configuring which sections of a post-install script
  33555. * will be run based on the user's setup, which is very useful for making flexible
  33556. * post-install scripts without losing the cross-Frontend ability to retrieve user input
  33557. * @param string
  33558. */
  33559. function skipParamgroup($id)
  33560. {
  33561. $this->_skipSections[$id] = true;
  33562. }
  33563. function runPostinstallScripts(&$scripts)
  33564. {
  33565. foreach ($scripts as $i => $script) {
  33566. $this->runInstallScript($scripts[$i]->_params, $scripts[$i]->_obj);
  33567. }
  33568. }
  33569. /**
  33570. * @param array $xml contents of postinstallscript tag
  33571. * @param object $script post-installation script
  33572. * @param string install|upgrade
  33573. */
  33574. function runInstallScript($xml, &$script)
  33575. {
  33576. $this->_skipSections = array();
  33577. if (!is_array($xml) || !isset($xml['paramgroup'])) {
  33578. $script->run(array(), '_default');
  33579. return;
  33580. }
  33581. $completedPhases = array();
  33582. if (!isset($xml['paramgroup'][0])) {
  33583. $xml['paramgroup'] = array($xml['paramgroup']);
  33584. }
  33585. foreach ($xml['paramgroup'] as $group) {
  33586. if (isset($this->_skipSections[$group['id']])) {
  33587. // the post-install script chose to skip this section dynamically
  33588. continue;
  33589. }
  33590. if (isset($group['name'])) {
  33591. $paramname = explode('::', $group['name']);
  33592. if ($lastgroup['id'] != $paramname[0]) {
  33593. continue;
  33594. }
  33595. $group['name'] = $paramname[1];
  33596. if (!isset($answers)) {
  33597. return;
  33598. }
  33599. if (isset($answers[$group['name']])) {
  33600. switch ($group['conditiontype']) {
  33601. case '=' :
  33602. if ($answers[$group['name']] != $group['value']) {
  33603. continue 2;
  33604. }
  33605. break;
  33606. case '!=' :
  33607. if ($answers[$group['name']] == $group['value']) {
  33608. continue 2;
  33609. }
  33610. break;
  33611. case 'preg_match' :
  33612. if (!@preg_match('/' . $group['value'] . '/',
  33613. $answers[$group['name']])) {
  33614. continue 2;
  33615. }
  33616. break;
  33617. default :
  33618. return;
  33619. }
  33620. }
  33621. }
  33622. $lastgroup = $group;
  33623. if (isset($group['instructions'])) {
  33624. $this->_display($group['instructions']);
  33625. }
  33626. if (!isset($group['param'][0])) {
  33627. $group['param'] = array($group['param']);
  33628. }
  33629. if (isset($group['param'])) {
  33630. if (method_exists($script, 'postProcessPrompts')) {
  33631. $prompts = $script->postProcessPrompts($group['param'], $group['id']);
  33632. if (!is_array($prompts) || count($prompts) != count($group['param'])) {
  33633. $this->outputData('postinstall', 'Error: post-install script did not ' .
  33634. 'return proper post-processed prompts');
  33635. $prompts = $group['param'];
  33636. } else {
  33637. foreach ($prompts as $i => $var) {
  33638. if (!is_array($var) || !isset($var['prompt']) ||
  33639. !isset($var['name']) ||
  33640. ($var['name'] != $group['param'][$i]['name']) ||
  33641. ($var['type'] != $group['param'][$i]['type'])
  33642. ) {
  33643. $this->outputData('postinstall', 'Error: post-install script ' .
  33644. 'modified the variables or prompts, severe security risk. ' .
  33645. 'Will instead use the defaults from the package.xml');
  33646. $prompts = $group['param'];
  33647. }
  33648. }
  33649. }
  33650. $answers = $this->confirmDialog($prompts);
  33651. } else {
  33652. $answers = $this->confirmDialog($group['param']);
  33653. }
  33654. }
  33655. if ((isset($answers) && $answers) || !isset($group['param'])) {
  33656. if (!isset($answers)) {
  33657. $answers = array();
  33658. }
  33659. array_unshift($completedPhases, $group['id']);
  33660. if (!$script->run($answers, $group['id'])) {
  33661. $script->run($completedPhases, '_undoOnError');
  33662. return;
  33663. }
  33664. } else {
  33665. $script->run($completedPhases, '_undoOnError');
  33666. return;
  33667. }
  33668. }
  33669. }
  33670. /**
  33671. * Ask for user input, confirm the answers and continue until the user is satisfied
  33672. * @param array an array of arrays, format array('name' => 'paramname', 'prompt' =>
  33673. * 'text to display', 'type' => 'string'[, default => 'default value'])
  33674. * @return array
  33675. */
  33676. function confirmDialog($params)
  33677. {
  33678. $answers = $prompts = $types = array();
  33679. foreach ($params as $param) {
  33680. $prompts[$param['name']] = $param['prompt'];
  33681. $types[$param['name']] = $param['type'];
  33682. $answers[$param['name']] = isset($param['default']) ? $param['default'] : '';
  33683. }
  33684. $tried = false;
  33685. do {
  33686. if ($tried) {
  33687. $i = 1;
  33688. foreach ($answers as $var => $value) {
  33689. if (!strlen($value)) {
  33690. echo $this->bold("* Enter an answer for #" . $i . ": ({$prompts[$var]})\n");
  33691. }
  33692. $i++;
  33693. }
  33694. }
  33695. $answers = $this->userDialog('', $prompts, $types, $answers);
  33696. $tried = true;
  33697. } while (is_array($answers) && count(array_filter($answers)) != count($prompts));
  33698. return $answers;
  33699. }
  33700. function userDialog($command, $prompts, $types = array(), $defaults = array(), $screensize = 20)
  33701. {
  33702. if (!is_array($prompts)) {
  33703. return array();
  33704. }
  33705. $testprompts = array_keys($prompts);
  33706. $result = $defaults;
  33707. reset($prompts);
  33708. if (count($prompts) === 1) {
  33709. foreach ($prompts as $key => $prompt) {
  33710. $type = $types[$key];
  33711. $default = @$defaults[$key];
  33712. print "$prompt ";
  33713. if ($default) {
  33714. print "[$default] ";
  33715. }
  33716. print ": ";
  33717. $line = fgets(STDIN, 2048);
  33718. $result[$key] = ($default && trim($line) == '') ? $default : trim($line);
  33719. }
  33720. return $result;
  33721. }
  33722. $first_run = true;
  33723. while (true) {
  33724. $descLength = max(array_map('strlen', $prompts));
  33725. $descFormat = "%-{$descLength}s";
  33726. $last = count($prompts);
  33727. $i = 0;
  33728. foreach ($prompts as $n => $var) {
  33729. $res = isset($result[$n]) ? $result[$n] : null;
  33730. printf("%2d. $descFormat : %s\n", ++$i, $prompts[$n], $res);
  33731. }
  33732. print "\n1-$last, 'all', 'abort', or Enter to continue: ";
  33733. $tmp = trim(fgets(STDIN, 1024));
  33734. if (empty($tmp)) {
  33735. break;
  33736. }
  33737. if ($tmp == 'abort') {
  33738. return false;
  33739. }
  33740. if (isset($testprompts[(int)$tmp - 1])) {
  33741. $var = $testprompts[(int)$tmp - 1];
  33742. $desc = $prompts[$var];
  33743. $current = @$result[$var];
  33744. print "$desc [$current] : ";
  33745. $tmp = trim(fgets(STDIN, 1024));
  33746. if ($tmp !== '') {
  33747. $result[$var] = $tmp;
  33748. }
  33749. } elseif ($tmp == 'all') {
  33750. foreach ($prompts as $var => $desc) {
  33751. $current = $result[$var];
  33752. print "$desc [$current] : ";
  33753. $tmp = trim(fgets(STDIN, 1024));
  33754. if (trim($tmp) !== '') {
  33755. $result[$var] = trim($tmp);
  33756. }
  33757. }
  33758. }
  33759. $first_run = false;
  33760. }
  33761. return $result;
  33762. }
  33763. function userConfirm($prompt, $default = 'yes')
  33764. {
  33765. trigger_error("PEAR_Frontend_CLI::userConfirm not yet converted", E_USER_ERROR);
  33766. static $positives = array('y', 'yes', 'on', '1');
  33767. static $negatives = array('n', 'no', 'off', '0');
  33768. print "$this->lp$prompt [$default] : ";
  33769. $fp = fopen("php://stdin", "r");
  33770. $line = fgets($fp, 2048);
  33771. fclose($fp);
  33772. $answer = strtolower(trim($line));
  33773. if (empty($answer)) {
  33774. $answer = $default;
  33775. }
  33776. if (in_array($answer, $positives)) {
  33777. return true;
  33778. }
  33779. if (in_array($answer, $negatives)) {
  33780. return false;
  33781. }
  33782. if (in_array($default, $positives)) {
  33783. return true;
  33784. }
  33785. return false;
  33786. }
  33787. function outputData($data, $command = '_default')
  33788. {
  33789. switch ($command) {
  33790. case 'channel-info':
  33791. foreach ($data as $type => $section) {
  33792. if ($type == 'main') {
  33793. $section['data'] = array_values($section['data']);
  33794. }
  33795. $this->outputData($section);
  33796. }
  33797. break;
  33798. case 'install':
  33799. case 'upgrade':
  33800. case 'upgrade-all':
  33801. if (is_array($data) && isset($data['release_warnings'])) {
  33802. $this->_displayLine('');
  33803. $this->_startTable(array(
  33804. 'border' => false,
  33805. 'caption' => 'Release Warnings'
  33806. ));
  33807. $this->_tableRow(array($data['release_warnings']), null, array(1 => array('wrap' => 55)));
  33808. $this->_endTable();
  33809. $this->_displayLine('');
  33810. }
  33811. $this->_displayLine(is_array($data) ? $data['data'] : $data);
  33812. break;
  33813. case 'search':
  33814. $this->_startTable($data);
  33815. if (isset($data['headline']) && is_array($data['headline'])) {
  33816. $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
  33817. }
  33818. $packages = array();
  33819. foreach($data['data'] as $category) {
  33820. foreach($category as $name => $pkg) {
  33821. $packages[$pkg[0]] = $pkg;
  33822. }
  33823. }
  33824. $p = array_keys($packages);
  33825. natcasesort($p);
  33826. foreach ($p as $name) {
  33827. $this->_tableRow($packages[$name], null, array(1 => array('wrap' => 55)));
  33828. }
  33829. $this->_endTable();
  33830. break;
  33831. case 'list-all':
  33832. if (!isset($data['data'])) {
  33833. $this->_displayLine('No packages in channel');
  33834. break;
  33835. }
  33836. $this->_startTable($data);
  33837. if (isset($data['headline']) && is_array($data['headline'])) {
  33838. $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
  33839. }
  33840. $packages = array();
  33841. foreach($data['data'] as $category) {
  33842. foreach($category as $name => $pkg) {
  33843. $packages[$pkg[0]] = $pkg;
  33844. }
  33845. }
  33846. $p = array_keys($packages);
  33847. natcasesort($p);
  33848. foreach ($p as $name) {
  33849. $pkg = $packages[$name];
  33850. unset($pkg[4], $pkg[5]);
  33851. $this->_tableRow($pkg, null, array(1 => array('wrap' => 55)));
  33852. }
  33853. $this->_endTable();
  33854. break;
  33855. case 'config-show':
  33856. $data['border'] = false;
  33857. $opts = array(
  33858. 0 => array('wrap' => 30),
  33859. 1 => array('wrap' => 20),
  33860. 2 => array('wrap' => 35)
  33861. );
  33862. $this->_startTable($data);
  33863. if (isset($data['headline']) && is_array($data['headline'])) {
  33864. $this->_tableRow($data['headline'], array('bold' => true), $opts);
  33865. }
  33866. foreach ($data['data'] as $group) {
  33867. foreach ($group as $value) {
  33868. if ($value[2] == '') {
  33869. $value[2] = "<not set>";
  33870. }
  33871. $this->_tableRow($value, null, $opts);
  33872. }
  33873. }
  33874. $this->_endTable();
  33875. break;
  33876. case 'remote-info':
  33877. $d = $data;
  33878. $data = array(
  33879. 'caption' => 'Package details:',
  33880. 'border' => false,
  33881. 'data' => array(
  33882. array("Latest", $data['stable']),
  33883. array("Installed", $data['installed']),
  33884. array("Package", $data['name']),
  33885. array("License", $data['license']),
  33886. array("Category", $data['category']),
  33887. array("Summary", $data['summary']),
  33888. array("Description", $data['description']),
  33889. ),
  33890. );
  33891. if (isset($d['deprecated']) && $d['deprecated']) {
  33892. $conf = &PEAR_Config::singleton();
  33893. $reg = $conf->getRegistry();
  33894. $name = $reg->parsedPackageNameToString($d['deprecated'], true);
  33895. $data['data'][] = array('Deprecated! use', $name);
  33896. }
  33897. default: {
  33898. if (is_array($data)) {
  33899. $this->_startTable($data);
  33900. $count = count($data['data'][0]);
  33901. if ($count == 2) {
  33902. $opts = array(0 => array('wrap' => 25),
  33903. 1 => array('wrap' => 48)
  33904. );
  33905. } elseif ($count == 3) {
  33906. $opts = array(0 => array('wrap' => 30),
  33907. 1 => array('wrap' => 20),
  33908. 2 => array('wrap' => 35)
  33909. );
  33910. } else {
  33911. $opts = null;
  33912. }
  33913. if (isset($data['headline']) && is_array($data['headline'])) {
  33914. $this->_tableRow($data['headline'],
  33915. array('bold' => true),
  33916. $opts);
  33917. }
  33918. if (is_array($data['data'])) {
  33919. foreach($data['data'] as $row) {
  33920. $this->_tableRow($row, null, $opts);
  33921. }
  33922. } else {
  33923. $this->_tableRow(array($data['data']), null, $opts);
  33924. }
  33925. $this->_endTable();
  33926. } else {
  33927. $this->_displayLine($data);
  33928. }
  33929. }
  33930. }
  33931. }
  33932. function log($text, $append_crlf = true)
  33933. {
  33934. if ($append_crlf) {
  33935. return $this->_displayLine($text);
  33936. }
  33937. return $this->_display($text);
  33938. }
  33939. function bold($text)
  33940. {
  33941. if (empty($this->term['bold'])) {
  33942. return strtoupper($text);
  33943. }
  33944. return $this->term['bold'] . $text . $this->term['normal'];
  33945. }
  33946. function _displayHeading($title)
  33947. {
  33948. print $this->lp.$this->bold($title)."\n";
  33949. print $this->lp.str_repeat("=", strlen($title))."\n";
  33950. }
  33951. function _startTable($params = array())
  33952. {
  33953. $params['table_data'] = array();
  33954. $params['widest'] = array(); // indexed by column
  33955. $params['highest'] = array(); // indexed by row
  33956. $params['ncols'] = 0;
  33957. $this->params = $params;
  33958. }
  33959. function _tableRow($columns, $rowparams = array(), $colparams = array())
  33960. {
  33961. $highest = 1;
  33962. for ($i = 0; $i < count($columns); $i++) {
  33963. $col = &$columns[$i];
  33964. if (isset($colparams[$i]) && !empty($colparams[$i]['wrap'])) {
  33965. $col = wordwrap($col, $colparams[$i]['wrap']);
  33966. }
  33967. if (strpos($col, "\n") !== false) {
  33968. $multiline = explode("\n", $col);
  33969. $w = 0;
  33970. foreach ($multiline as $n => $line) {
  33971. $len = strlen($line);
  33972. if ($len > $w) {
  33973. $w = $len;
  33974. }
  33975. }
  33976. $lines = count($multiline);
  33977. } else {
  33978. $w = strlen($col);
  33979. }
  33980. if (isset($this->params['widest'][$i])) {
  33981. if ($w > $this->params['widest'][$i]) {
  33982. $this->params['widest'][$i] = $w;
  33983. }
  33984. } else {
  33985. $this->params['widest'][$i] = $w;
  33986. }
  33987. $tmp = count_chars($columns[$i], 1);
  33988. // handle unix, mac and windows formats
  33989. $lines = (isset($tmp[10]) ? $tmp[10] : (isset($tmp[13]) ? $tmp[13] : 0)) + 1;
  33990. if ($lines > $highest) {
  33991. $highest = $lines;
  33992. }
  33993. }
  33994. if (count($columns) > $this->params['ncols']) {
  33995. $this->params['ncols'] = count($columns);
  33996. }
  33997. $new_row = array(
  33998. 'data' => $columns,
  33999. 'height' => $highest,
  34000. 'rowparams' => $rowparams,
  34001. 'colparams' => $colparams,
  34002. );
  34003. $this->params['table_data'][] = $new_row;
  34004. }
  34005. function _endTable()
  34006. {
  34007. extract($this->params);
  34008. if (!empty($caption)) {
  34009. $this->_displayHeading($caption);
  34010. }
  34011. if (count($table_data) === 0) {
  34012. return;
  34013. }
  34014. if (!isset($width)) {
  34015. $width = $widest;
  34016. } else {
  34017. for ($i = 0; $i < $ncols; $i++) {
  34018. if (!isset($width[$i])) {
  34019. $width[$i] = $widest[$i];
  34020. }
  34021. }
  34022. }
  34023. $border = false;
  34024. if (empty($border)) {
  34025. $cellstart = '';
  34026. $cellend = ' ';
  34027. $rowend = '';
  34028. $padrowend = false;
  34029. $borderline = '';
  34030. } else {
  34031. $cellstart = '| ';
  34032. $cellend = ' ';
  34033. $rowend = '|';
  34034. $padrowend = true;
  34035. $borderline = '+';
  34036. foreach ($width as $w) {
  34037. $borderline .= str_repeat('-', $w + strlen($cellstart) + strlen($cellend) - 1);
  34038. $borderline .= '+';
  34039. }
  34040. }
  34041. if ($borderline) {
  34042. $this->_displayLine($borderline);
  34043. }
  34044. for ($i = 0; $i < count($table_data); $i++) {
  34045. extract($table_data[$i]);
  34046. if (!is_array($rowparams)) {
  34047. $rowparams = array();
  34048. }
  34049. if (!is_array($colparams)) {
  34050. $colparams = array();
  34051. }
  34052. $rowlines = array();
  34053. if ($height > 1) {
  34054. for ($c = 0; $c < count($data); $c++) {
  34055. $rowlines[$c] = preg_split('/(\r?\n|\r)/', $data[$c]);
  34056. if (count($rowlines[$c]) < $height) {
  34057. $rowlines[$c] = array_pad($rowlines[$c], $height, '');
  34058. }
  34059. }
  34060. } else {
  34061. for ($c = 0; $c < count($data); $c++) {
  34062. $rowlines[$c] = array($data[$c]);
  34063. }
  34064. }
  34065. for ($r = 0; $r < $height; $r++) {
  34066. $rowtext = '';
  34067. for ($c = 0; $c < count($data); $c++) {
  34068. if (isset($colparams[$c])) {
  34069. $attribs = array_merge($rowparams, $colparams);
  34070. } else {
  34071. $attribs = $rowparams;
  34072. }
  34073. $w = isset($width[$c]) ? $width[$c] : 0;
  34074. //$cell = $data[$c];
  34075. $cell = $rowlines[$c][$r];
  34076. $l = strlen($cell);
  34077. if ($l > $w) {
  34078. $cell = substr($cell, 0, $w);
  34079. }
  34080. if (isset($attribs['bold'])) {
  34081. $cell = $this->bold($cell);
  34082. }
  34083. if ($l < $w) {
  34084. // not using str_pad here because we may
  34085. // add bold escape characters to $cell
  34086. $cell .= str_repeat(' ', $w - $l);
  34087. }
  34088. $rowtext .= $cellstart . $cell . $cellend;
  34089. }
  34090. if (!$border) {
  34091. $rowtext = rtrim($rowtext);
  34092. }
  34093. $rowtext .= $rowend;
  34094. $this->_displayLine($rowtext);
  34095. }
  34096. }
  34097. if ($borderline) {
  34098. $this->_displayLine($borderline);
  34099. }
  34100. }
  34101. function _displayLine($text)
  34102. {
  34103. print "$this->lp$text\n";
  34104. }
  34105. function _display($text)
  34106. {
  34107. print $text;
  34108. }
  34109. }
  34110. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Common.php���������������������������������������������������������0000644�0001750�0001750�00000014107�14720722517�017617� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  34111. /**
  34112. * Base class for all installation roles.
  34113. *
  34114. * PHP versions 4 and 5
  34115. *
  34116. * @category pear
  34117. * @package PEAR
  34118. * @author Greg Beaver <cellog@php.net>
  34119. * @copyright 1997-2006 The PHP Group
  34120. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34121. * @link http://pear.php.net/package/PEAR
  34122. * @since File available since Release 1.4.0a1
  34123. */
  34124. /**
  34125. * Base class for all installation roles.
  34126. *
  34127. * This class allows extensibility of file roles. Packages with complex
  34128. * customization can now provide custom file roles along with the possibility of
  34129. * adding configuration values to match.
  34130. * @category pear
  34131. * @package PEAR
  34132. * @author Greg Beaver <cellog@php.net>
  34133. * @copyright 1997-2006 The PHP Group
  34134. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34135. * @version Release: 1.10.16
  34136. * @link http://pear.php.net/package/PEAR
  34137. * @since Class available since Release 1.4.0a1
  34138. */
  34139. class PEAR_Installer_Role_Common
  34140. {
  34141. /**
  34142. * @var PEAR_Config
  34143. * @access protected
  34144. */
  34145. var $config;
  34146. /**
  34147. * @param PEAR_Config
  34148. */
  34149. function __construct(&$config)
  34150. {
  34151. $this->config = $config;
  34152. }
  34153. /**
  34154. * Retrieve configuration information about a file role from its XML info
  34155. *
  34156. * @param string $role Role Classname, as in "PEAR_Installer_Role_Data"
  34157. * @return array
  34158. */
  34159. function getInfo($role)
  34160. {
  34161. if (empty($GLOBALS['_PEAR_INSTALLER_ROLES'][$role])) {
  34162. return PEAR::raiseError('Unknown Role class: "' . $role . '"');
  34163. }
  34164. return $GLOBALS['_PEAR_INSTALLER_ROLES'][$role];
  34165. }
  34166. /**
  34167. * This is called for each file to set up the directories and files
  34168. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  34169. * @param array attributes from the <file> tag
  34170. * @param string file name
  34171. * @return array an array consisting of:
  34172. *
  34173. * 1 the original, pre-baseinstalldir installation directory
  34174. * 2 the final installation directory
  34175. * 3 the full path to the final location of the file
  34176. * 4 the location of the pre-installation file
  34177. */
  34178. function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null)
  34179. {
  34180. $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' .
  34181. ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
  34182. if (PEAR::isError($roleInfo)) {
  34183. return $roleInfo;
  34184. }
  34185. if (!$roleInfo['locationconfig']) {
  34186. return false;
  34187. }
  34188. if ($roleInfo['honorsbaseinstall']) {
  34189. $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'], $layer,
  34190. $pkg->getChannel());
  34191. if (!empty($atts['baseinstalldir'])) {
  34192. $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
  34193. }
  34194. } elseif ($roleInfo['unusualbaseinstall']) {
  34195. $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'],
  34196. $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage();
  34197. if (!empty($atts['baseinstalldir'])) {
  34198. $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
  34199. }
  34200. } else {
  34201. $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'],
  34202. $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage();
  34203. }
  34204. if (dirname($file) != '.' && empty($atts['install-as'])) {
  34205. $dest_dir .= DIRECTORY_SEPARATOR . dirname($file);
  34206. }
  34207. if (empty($atts['install-as'])) {
  34208. $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file);
  34209. } else {
  34210. $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as'];
  34211. }
  34212. $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file;
  34213. // Clean up the DIRECTORY_SEPARATOR mess
  34214. $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
  34215. list($dest_dir, $dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
  34216. array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR,
  34217. DIRECTORY_SEPARATOR),
  34218. array($dest_dir, $dest_file, $orig_file));
  34219. return array($save_destdir, $dest_dir, $dest_file, $orig_file);
  34220. }
  34221. /**
  34222. * Get the name of the configuration variable that specifies the location of this file
  34223. * @return string|false
  34224. */
  34225. function getLocationConfig()
  34226. {
  34227. $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' .
  34228. ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
  34229. if (PEAR::isError($roleInfo)) {
  34230. return $roleInfo;
  34231. }
  34232. return $roleInfo['locationconfig'];
  34233. }
  34234. /**
  34235. * Do any unusual setup here
  34236. * @param PEAR_Installer
  34237. * @param PEAR_PackageFile_v2
  34238. * @param array file attributes
  34239. * @param string file name
  34240. */
  34241. function setup(&$installer, $pkg, $atts, $file)
  34242. {
  34243. }
  34244. function isExecutable()
  34245. {
  34246. $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' .
  34247. ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
  34248. if (PEAR::isError($roleInfo)) {
  34249. return $roleInfo;
  34250. }
  34251. return $roleInfo['executable'];
  34252. }
  34253. function isInstallable()
  34254. {
  34255. $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' .
  34256. ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
  34257. if (PEAR::isError($roleInfo)) {
  34258. return $roleInfo;
  34259. }
  34260. return $roleInfo['installable'];
  34261. }
  34262. function isExtension()
  34263. {
  34264. $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' .
  34265. ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
  34266. if (PEAR::isError($roleInfo)) {
  34267. return $roleInfo;
  34268. }
  34269. return $roleInfo['phpextension'];
  34270. }
  34271. }
  34272. ?>
  34273. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Cfg.xml������������������������������������������������������������0000644�0001750�0001750�00000000645�14720722517�017101� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<role version="1.0">
  34274. <releasetypes>php</releasetypes>
  34275. <releasetypes>extsrc</releasetypes>
  34276. <releasetypes>extbin</releasetypes>
  34277. <releasetypes>zendextsrc</releasetypes>
  34278. <releasetypes>zendextbin</releasetypes>
  34279. <installable>1</installable>
  34280. <locationconfig>cfg_dir</locationconfig>
  34281. <honorsbaseinstall />
  34282. <unusualbaseinstall>1</unusualbaseinstall>
  34283. <phpfile />
  34284. <executable />
  34285. <phpextension />
  34286. <config_vars />
  34287. </role>�������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Cfg.php������������������������������������������������������������0000644�0001750�0001750�00000007577�14720722517�017103� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  34288. /**
  34289. * PEAR_Installer_Role_Cfg
  34290. *
  34291. * PHP versions 4 and 5
  34292. *
  34293. * @category pear
  34294. * @package PEAR
  34295. * @author Greg Beaver <cellog@php.net>
  34296. * @copyright 2007-2009 The Authors
  34297. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34298. * @link http://pear.php.net/package/PEAR
  34299. * @since File available since Release 1.7.0
  34300. */
  34301. /**
  34302. * @category pear
  34303. * @package PEAR
  34304. * @author Greg Beaver <cellog@php.net>
  34305. * @copyright 2007-2009 The Authors
  34306. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34307. * @version Release: 1.10.16
  34308. * @link http://pear.php.net/package/PEAR
  34309. * @since Class available since Release 1.7.0
  34310. */
  34311. class PEAR_Installer_Role_Cfg extends PEAR_Installer_Role_Common
  34312. {
  34313. /**
  34314. * @var PEAR_Installer
  34315. */
  34316. var $installer;
  34317. /**
  34318. * the md5 of the original file
  34319. *
  34320. * @var unknown_type
  34321. */
  34322. var $md5 = null;
  34323. /**
  34324. * Do any unusual setup here
  34325. * @param PEAR_Installer
  34326. * @param PEAR_PackageFile_v2
  34327. * @param array file attributes
  34328. * @param string file name
  34329. */
  34330. function setup(&$installer, $pkg, $atts, $file)
  34331. {
  34332. $this->installer = &$installer;
  34333. $reg = &$this->installer->config->getRegistry();
  34334. $package = $reg->getPackage($pkg->getPackage(), $pkg->getChannel());
  34335. if ($package) {
  34336. $filelist = $package->getFilelist();
  34337. if (isset($filelist[$file]) && isset($filelist[$file]['md5sum'])) {
  34338. $this->md5 = $filelist[$file]['md5sum'];
  34339. }
  34340. }
  34341. }
  34342. function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null)
  34343. {
  34344. $test = parent::processInstallation($pkg, $atts, $file, $tmp_path, $layer);
  34345. if (@file_exists($test[2]) && @file_exists($test[3])) {
  34346. $md5 = md5_file($test[2]);
  34347. // configuration has already been installed, check for mods
  34348. if ($md5 !== $this->md5 && $md5 !== md5_file($test[3])) {
  34349. // configuration has been modified, so save our version as
  34350. // configfile-version
  34351. $old = $test[2];
  34352. $test[2] .= '.new-' . $pkg->getVersion();
  34353. // backup original and re-install it
  34354. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  34355. $tmpcfg = $this->config->get('temp_dir');
  34356. $newloc = System::mkdir(array('-p', $tmpcfg));
  34357. if (!$newloc) {
  34358. // try temp_dir
  34359. $newloc = System::mktemp(array('-d'));
  34360. if (!$newloc || PEAR::isError($newloc)) {
  34361. PEAR::popErrorHandling();
  34362. return PEAR::raiseError('Could not save existing configuration file '.
  34363. $old . ', unable to install. Please set temp_dir ' .
  34364. 'configuration variable to a writeable location and try again');
  34365. }
  34366. } else {
  34367. $newloc = $tmpcfg;
  34368. }
  34369. $temp_file = $newloc . DIRECTORY_SEPARATOR . uniqid('savefile');
  34370. if (!@copy($old, $temp_file)) {
  34371. PEAR::popErrorHandling();
  34372. return PEAR::raiseError('Could not save existing configuration file '.
  34373. $old . ', unable to install. Please set temp_dir ' .
  34374. 'configuration variable to a writeable location and try again');
  34375. }
  34376. PEAR::popErrorHandling();
  34377. $this->installer->log(0, "WARNING: configuration file $old is being installed as $test[2], you should manually merge in changes to the existing configuration file");
  34378. $this->installer->addFileOperation('rename', array($temp_file, $old, false));
  34379. $this->installer->addFileOperation('delete', array($temp_file));
  34380. }
  34381. }
  34382. return $test;
  34383. }
  34384. }���������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Data.xml�����������������������������������������������������������0000644�0001750�0001750�00000000622�14720722517�017246� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<role version="1.0">
  34385. <releasetypes>php</releasetypes>
  34386. <releasetypes>extsrc</releasetypes>
  34387. <releasetypes>extbin</releasetypes>
  34388. <releasetypes>zendextsrc</releasetypes>
  34389. <releasetypes>zendextbin</releasetypes>
  34390. <installable>1</installable>
  34391. <locationconfig>data_dir</locationconfig>
  34392. <honorsbaseinstall />
  34393. <unusualbaseinstall />
  34394. <phpfile />
  34395. <executable />
  34396. <phpextension />
  34397. <config_vars />
  34398. </role>��������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Data.php�����������������������������������������������������������0000644�0001750�0001750�00000001417�14720722517�017240� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  34399. /**
  34400. * PEAR_Installer_Role_Data
  34401. *
  34402. * PHP versions 4 and 5
  34403. *
  34404. * @category pear
  34405. * @package PEAR
  34406. * @author Greg Beaver <cellog@php.net>
  34407. * @copyright 1997-2009 The Authors
  34408. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34409. * @link http://pear.php.net/package/PEAR
  34410. * @since File available since Release 1.4.0a1
  34411. */
  34412. /**
  34413. * @category pear
  34414. * @package PEAR
  34415. * @author Greg Beaver <cellog@php.net>
  34416. * @copyright 1997-2009 The Authors
  34417. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34418. * @version Release: 1.10.16
  34419. * @link http://pear.php.net/package/PEAR
  34420. * @since Class available since Release 1.4.0a1
  34421. */
  34422. class PEAR_Installer_Role_Data extends PEAR_Installer_Role_Common {}
  34423. ?>�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Doc.xml������������������������������������������������������������0000644�0001750�0001750�00000000621�14720722517�017101� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<role version="1.0">
  34424. <releasetypes>php</releasetypes>
  34425. <releasetypes>extsrc</releasetypes>
  34426. <releasetypes>extbin</releasetypes>
  34427. <releasetypes>zendextsrc</releasetypes>
  34428. <releasetypes>zendextbin</releasetypes>
  34429. <installable>1</installable>
  34430. <locationconfig>doc_dir</locationconfig>
  34431. <honorsbaseinstall />
  34432. <unusualbaseinstall />
  34433. <phpfile />
  34434. <executable />
  34435. <phpextension />
  34436. <config_vars />
  34437. </role>���������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Doc.php������������������������������������������������������������0000644�0001750�0001750�00000001415�14720722517�017072� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  34438. /**
  34439. * PEAR_Installer_Role_Doc
  34440. *
  34441. * PHP versions 4 and 5
  34442. *
  34443. * @category pear
  34444. * @package PEAR
  34445. * @author Greg Beaver <cellog@php.net>
  34446. * @copyright 1997-2009 The Authors
  34447. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34448. * @link http://pear.php.net/package/PEAR
  34449. * @since File available since Release 1.4.0a1
  34450. */
  34451. /**
  34452. * @category pear
  34453. * @package PEAR
  34454. * @author Greg Beaver <cellog@php.net>
  34455. * @copyright 1997-2009 The Authors
  34456. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34457. * @version Release: 1.10.16
  34458. * @link http://pear.php.net/package/PEAR
  34459. * @since Class available since Release 1.4.0a1
  34460. */
  34461. class PEAR_Installer_Role_Doc extends PEAR_Installer_Role_Common {}
  34462. ?>���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Ext.xml������������������������������������������������������������0000644�0001750�0001750�00000000502�14720722517�017132� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<role version="1.0">
  34463. <releasetypes>extbin</releasetypes>
  34464. <releasetypes>zendextbin</releasetypes>
  34465. <installable>1</installable>
  34466. <locationconfig>ext_dir</locationconfig>
  34467. <honorsbaseinstall>1</honorsbaseinstall>
  34468. <unusualbaseinstall />
  34469. <phpfile />
  34470. <executable />
  34471. <phpextension>1</phpextension>
  34472. <config_vars />
  34473. </role>����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Ext.php������������������������������������������������������������0000644�0001750�0001750�00000001415�14720722517�017125� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  34474. /**
  34475. * PEAR_Installer_Role_Ext
  34476. *
  34477. * PHP versions 4 and 5
  34478. *
  34479. * @category pear
  34480. * @package PEAR
  34481. * @author Greg Beaver <cellog@php.net>
  34482. * @copyright 1997-2009 The Authors
  34483. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34484. * @link http://pear.php.net/package/PEAR
  34485. * @since File available since Release 1.4.0a1
  34486. */
  34487. /**
  34488. * @category pear
  34489. * @package PEAR
  34490. * @author Greg Beaver <cellog@php.net>
  34491. * @copyright 1997-2009 The Authors
  34492. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34493. * @version Release: 1.10.16
  34494. * @link http://pear.php.net/package/PEAR
  34495. * @since Class available since Release 1.4.0a1
  34496. */
  34497. class PEAR_Installer_Role_Ext extends PEAR_Installer_Role_Common {}
  34498. ?>���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Man.xml������������������������������������������������������������0000644�0001750�0001750�00000000645�14720722517�017115� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<role version="1.0">
  34499. <releasetypes>php</releasetypes>
  34500. <releasetypes>extsrc</releasetypes>
  34501. <releasetypes>extbin</releasetypes>
  34502. <releasetypes>zendextsrc</releasetypes>
  34503. <releasetypes>zendextbin</releasetypes>
  34504. <installable>1</installable>
  34505. <locationconfig>man_dir</locationconfig>
  34506. <honorsbaseinstall>1</honorsbaseinstall>
  34507. <unusualbaseinstall />
  34508. <phpfile />
  34509. <executable />
  34510. <phpextension />
  34511. <config_vars />
  34512. </role>
  34513. �������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Man.php������������������������������������������������������������0000644�0001750�0001750�00000001445�14720722517�017103� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  34514. /**
  34515. * PEAR_Installer_Role_Man
  34516. *
  34517. * PHP versions 4 and 5
  34518. *
  34519. * @category pear
  34520. * @package PEAR
  34521. * @author Hannes Magnusson <bjori@php.net>
  34522. * @copyright 2011 The Authors
  34523. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34524. * @version SVN: $Id: $
  34525. * @link http://pear.php.net/package/PEAR
  34526. * @since File available since Release 1.10.0
  34527. */
  34528. /**
  34529. * @category pear
  34530. * @package PEAR
  34531. * @author Hannes Magnusson <bjori@php.net>
  34532. * @copyright 2011 The Authors
  34533. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34534. * @version Release: 1.10.16
  34535. * @link http://pear.php.net/package/PEAR
  34536. * @since Class available since Release 1.10.0
  34537. */
  34538. class PEAR_Installer_Role_Man extends PEAR_Installer_Role_Common {}
  34539. ?>
  34540. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Php.xml������������������������������������������������������������0000644�0001750�0001750�00000000655�14720722517�017132� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<role version="1.0">
  34541. <releasetypes>php</releasetypes>
  34542. <releasetypes>extsrc</releasetypes>
  34543. <releasetypes>extbin</releasetypes>
  34544. <releasetypes>zendextsrc</releasetypes>
  34545. <releasetypes>zendextbin</releasetypes>
  34546. <installable>1</installable>
  34547. <locationconfig>php_dir</locationconfig>
  34548. <honorsbaseinstall>1</honorsbaseinstall>
  34549. <unusualbaseinstall />
  34550. <phpfile>1</phpfile>
  34551. <executable />
  34552. <phpextension />
  34553. <config_vars />
  34554. </role>�����������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Php.php������������������������������������������������������������0000644�0001750�0001750�00000001415�14720722517�017114� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  34555. /**
  34556. * PEAR_Installer_Role_Php
  34557. *
  34558. * PHP versions 4 and 5
  34559. *
  34560. * @category pear
  34561. * @package PEAR
  34562. * @author Greg Beaver <cellog@php.net>
  34563. * @copyright 1997-2009 The Authors
  34564. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34565. * @link http://pear.php.net/package/PEAR
  34566. * @since File available since Release 1.4.0a1
  34567. */
  34568. /**
  34569. * @category pear
  34570. * @package PEAR
  34571. * @author Greg Beaver <cellog@php.net>
  34572. * @copyright 1997-2009 The Authors
  34573. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34574. * @version Release: 1.10.16
  34575. * @link http://pear.php.net/package/PEAR
  34576. * @since Class available since Release 1.4.0a1
  34577. */
  34578. class PEAR_Installer_Role_Php extends PEAR_Installer_Role_Common {}
  34579. ?>���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Script.xml���������������������������������������������������������0000644�0001750�0001750�00000000660�14720722517�017643� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<role version="1.0">
  34580. <releasetypes>php</releasetypes>
  34581. <releasetypes>extsrc</releasetypes>
  34582. <releasetypes>extbin</releasetypes>
  34583. <releasetypes>zendextsrc</releasetypes>
  34584. <releasetypes>zendextbin</releasetypes>
  34585. <installable>1</installable>
  34586. <locationconfig>bin_dir</locationconfig>
  34587. <honorsbaseinstall>1</honorsbaseinstall>
  34588. <unusualbaseinstall />
  34589. <phpfile />
  34590. <executable>1</executable>
  34591. <phpextension />
  34592. <config_vars />
  34593. </role>��������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Script.php���������������������������������������������������������0000644�0001750�0001750�00000001423�14720722517�017630� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  34594. /**
  34595. * PEAR_Installer_Role_Script
  34596. *
  34597. * PHP versions 4 and 5
  34598. *
  34599. * @category pear
  34600. * @package PEAR
  34601. * @author Greg Beaver <cellog@php.net>
  34602. * @copyright 1997-2009 The Authors
  34603. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34604. * @link http://pear.php.net/package/PEAR
  34605. * @since File available since Release 1.4.0a1
  34606. */
  34607. /**
  34608. * @category pear
  34609. * @package PEAR
  34610. * @author Greg Beaver <cellog@php.net>
  34611. * @copyright 1997-2009 The Authors
  34612. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34613. * @version Release: 1.10.16
  34614. * @link http://pear.php.net/package/PEAR
  34615. * @since Class available since Release 1.4.0a1
  34616. */
  34617. class PEAR_Installer_Role_Script extends PEAR_Installer_Role_Common {}
  34618. ?>���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Src.xml������������������������������������������������������������0000644�0001750�0001750�00000000442�14720722517�017124� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<role version="1.0">
  34619. <releasetypes>extsrc</releasetypes>
  34620. <releasetypes>zendextsrc</releasetypes>
  34621. <installable>1</installable>
  34622. <locationconfig>temp_dir</locationconfig>
  34623. <honorsbaseinstall />
  34624. <unusualbaseinstall />
  34625. <phpfile />
  34626. <executable />
  34627. <phpextension />
  34628. <config_vars />
  34629. </role>������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Src.php������������������������������������������������������������0000644�0001750�0001750�00000001562�14720722517�017117� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  34630. /**
  34631. * PEAR_Installer_Role_Src
  34632. *
  34633. * PHP versions 4 and 5
  34634. *
  34635. * @category pear
  34636. * @package PEAR
  34637. * @author Greg Beaver <cellog@php.net>
  34638. * @copyright 1997-2009 The Authors
  34639. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34640. * @link http://pear.php.net/package/PEAR
  34641. * @since File available since Release 1.4.0a1
  34642. */
  34643. /**
  34644. * @category pear
  34645. * @package PEAR
  34646. * @author Greg Beaver <cellog@php.net>
  34647. * @copyright 1997-2009 The Authors
  34648. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34649. * @version Release: 1.10.16
  34650. * @link http://pear.php.net/package/PEAR
  34651. * @since Class available since Release 1.4.0a1
  34652. */
  34653. class PEAR_Installer_Role_Src extends PEAR_Installer_Role_Common
  34654. {
  34655. function setup(&$installer, $pkg, $atts, $file)
  34656. {
  34657. $installer->source_files++;
  34658. }
  34659. }
  34660. ?>����������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Test.xml�����������������������������������������������������������0000644�0001750�0001750�00000000622�14720722517�017314� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<role version="1.0">
  34661. <releasetypes>php</releasetypes>
  34662. <releasetypes>extsrc</releasetypes>
  34663. <releasetypes>extbin</releasetypes>
  34664. <releasetypes>zendextsrc</releasetypes>
  34665. <releasetypes>zendextbin</releasetypes>
  34666. <installable>1</installable>
  34667. <locationconfig>test_dir</locationconfig>
  34668. <honorsbaseinstall />
  34669. <unusualbaseinstall />
  34670. <phpfile />
  34671. <executable />
  34672. <phpextension />
  34673. <config_vars />
  34674. </role>��������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Test.php�����������������������������������������������������������0000644�0001750�0001750�00000001417�14720722517�017306� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  34675. /**
  34676. * PEAR_Installer_Role_Test
  34677. *
  34678. * PHP versions 4 and 5
  34679. *
  34680. * @category pear
  34681. * @package PEAR
  34682. * @author Greg Beaver <cellog@php.net>
  34683. * @copyright 1997-2009 The Authors
  34684. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34685. * @link http://pear.php.net/package/PEAR
  34686. * @since File available since Release 1.4.0a1
  34687. */
  34688. /**
  34689. * @category pear
  34690. * @package PEAR
  34691. * @author Greg Beaver <cellog@php.net>
  34692. * @copyright 1997-2009 The Authors
  34693. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34694. * @version Release: 1.10.16
  34695. * @link http://pear.php.net/package/PEAR
  34696. * @since Class available since Release 1.4.0a1
  34697. */
  34698. class PEAR_Installer_Role_Test extends PEAR_Installer_Role_Common {}
  34699. ?>�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Www.xml������������������������������������������������������������0000644�0001750�0001750�00000000644�14720722517�017165� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<role version="1.0">
  34700. <releasetypes>php</releasetypes>
  34701. <releasetypes>extsrc</releasetypes>
  34702. <releasetypes>extbin</releasetypes>
  34703. <releasetypes>zendextsrc</releasetypes>
  34704. <releasetypes>zendextbin</releasetypes>
  34705. <installable>1</installable>
  34706. <locationconfig>www_dir</locationconfig>
  34707. <honorsbaseinstall>1</honorsbaseinstall>
  34708. <unusualbaseinstall />
  34709. <phpfile />
  34710. <executable />
  34711. <phpextension />
  34712. <config_vars />
  34713. </role>��������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role/Www.php������������������������������������������������������������0000644�0001750�0001750�00000001411�14720722517�017145� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  34714. /**
  34715. * PEAR_Installer_Role_Www
  34716. *
  34717. * PHP versions 4 and 5
  34718. *
  34719. * @category pear
  34720. * @package PEAR
  34721. * @author Greg Beaver <cellog@php.net>
  34722. * @copyright 2007-2009 The Authors
  34723. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34724. * @link http://pear.php.net/package/PEAR
  34725. * @since File available since Release 1.7.0
  34726. */
  34727. /**
  34728. * @category pear
  34729. * @package PEAR
  34730. * @author Greg Beaver <cellog@php.net>
  34731. * @copyright 2007-2009 The Authors
  34732. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34733. * @version Release: 1.10.16
  34734. * @link http://pear.php.net/package/PEAR
  34735. * @since Class available since Release 1.7.0
  34736. */
  34737. class PEAR_Installer_Role_Www extends PEAR_Installer_Role_Common {}
  34738. ?>�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Installer/Role.php����������������������������������������������������������������0000664�0001750�0001750�00000017274�14720722517�016401� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  34739. /**
  34740. * PEAR_Installer_Role
  34741. *
  34742. * PHP versions 4 and 5
  34743. *
  34744. * @category pear
  34745. * @package PEAR
  34746. * @author Greg Beaver <cellog@php.net>
  34747. * @copyright 1997-2009 The Authors
  34748. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34749. * @link http://pear.php.net/package/PEAR
  34750. * @since File available since Release 1.4.0a1
  34751. */
  34752. /**
  34753. * base class for installer roles
  34754. */
  34755. require_once 'PEAR/Installer/Role/Common.php';
  34756. require_once 'PEAR/XMLParser.php';
  34757. /**
  34758. * @category pear
  34759. * @package PEAR
  34760. * @author Greg Beaver <cellog@php.net>
  34761. * @copyright 1997-2009 The Authors
  34762. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34763. * @version Release: 1.10.16
  34764. * @link http://pear.php.net/package/PEAR
  34765. * @since Class available since Release 1.4.0a1
  34766. */
  34767. class PEAR_Installer_Role
  34768. {
  34769. /**
  34770. * Set up any additional configuration variables that file roles require
  34771. *
  34772. * Never call this directly, it is called by the PEAR_Config constructor
  34773. * @param PEAR_Config
  34774. */
  34775. public static function initializeConfig(&$config)
  34776. {
  34777. if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
  34778. PEAR_Installer_Role::registerRoles();
  34779. }
  34780. foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $class => $info) {
  34781. if (!$info['config_vars']) {
  34782. continue;
  34783. }
  34784. $config->_addConfigVars($class, $info['config_vars']);
  34785. }
  34786. }
  34787. /**
  34788. * @param PEAR_PackageFile_v2
  34789. * @param string role name
  34790. * @param PEAR_Config
  34791. * @return PEAR_Installer_Role_Common
  34792. */
  34793. public static function &factory($pkg, $role, &$config)
  34794. {
  34795. if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
  34796. PEAR_Installer_Role::registerRoles();
  34797. }
  34798. if (!in_array($role, PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
  34799. $a = false;
  34800. return $a;
  34801. }
  34802. $a = 'PEAR_Installer_Role_' . ucfirst($role);
  34803. if (!class_exists($a)) {
  34804. require_once str_replace('_', '/', $a) . '.php';
  34805. }
  34806. $b = new $a($config);
  34807. return $b;
  34808. }
  34809. /**
  34810. * Get a list of file roles that are valid for the particular release type.
  34811. *
  34812. * For instance, src files serve no purpose in regular php releases.
  34813. * @param string
  34814. * @param bool clear cache
  34815. * @return array
  34816. */
  34817. public static function getValidRoles($release, $clear = false)
  34818. {
  34819. if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
  34820. PEAR_Installer_Role::registerRoles();
  34821. }
  34822. static $ret = array();
  34823. if ($clear) {
  34824. $ret = array();
  34825. }
  34826. if (isset($ret[$release])) {
  34827. return $ret[$release];
  34828. }
  34829. $ret[$release] = array();
  34830. foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
  34831. if (in_array($release, $okreleases['releasetypes'])) {
  34832. $ret[$release][] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
  34833. }
  34834. }
  34835. return $ret[$release];
  34836. }
  34837. /**
  34838. * Get a list of roles that require their files to be installed
  34839. *
  34840. * Most roles must be installed, but src and package roles, for instance
  34841. * are pseudo-roles. src files are compiled into a new extension. Package
  34842. * roles are actually fully bundled releases of a package
  34843. * @param bool clear cache
  34844. * @return array
  34845. */
  34846. public static function getInstallableRoles($clear = false)
  34847. {
  34848. if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
  34849. PEAR_Installer_Role::registerRoles();
  34850. }
  34851. static $ret;
  34852. if ($clear) {
  34853. unset($ret);
  34854. }
  34855. if (isset($ret)) {
  34856. return $ret;
  34857. }
  34858. $ret = array();
  34859. foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
  34860. if ($okreleases['installable']) {
  34861. $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
  34862. }
  34863. }
  34864. return $ret;
  34865. }
  34866. /**
  34867. * Return an array of roles that are affected by the baseinstalldir attribute
  34868. *
  34869. * Most roles ignore this attribute, and instead install directly into:
  34870. * PackageName/filepath
  34871. * so a tests file tests/file.phpt is installed into PackageName/tests/filepath.php
  34872. * @param bool clear cache
  34873. * @return array
  34874. */
  34875. public static function getBaseinstallRoles($clear = false)
  34876. {
  34877. if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
  34878. PEAR_Installer_Role::registerRoles();
  34879. }
  34880. static $ret;
  34881. if ($clear) {
  34882. unset($ret);
  34883. }
  34884. if (isset($ret)) {
  34885. return $ret;
  34886. }
  34887. $ret = array();
  34888. foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
  34889. if ($okreleases['honorsbaseinstall']) {
  34890. $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
  34891. }
  34892. }
  34893. return $ret;
  34894. }
  34895. /**
  34896. * Return an array of file roles that should be analyzed for PHP content at package time,
  34897. * like the "php" role.
  34898. * @param bool clear cache
  34899. * @return array
  34900. */
  34901. public static function getPhpRoles($clear = false)
  34902. {
  34903. if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
  34904. PEAR_Installer_Role::registerRoles();
  34905. }
  34906. static $ret;
  34907. if ($clear) {
  34908. unset($ret);
  34909. }
  34910. if (isset($ret)) {
  34911. return $ret;
  34912. }
  34913. $ret = array();
  34914. foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
  34915. if ($okreleases['phpfile']) {
  34916. $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
  34917. }
  34918. }
  34919. return $ret;
  34920. }
  34921. /**
  34922. * Scan through the Command directory looking for classes
  34923. * and see what commands they implement.
  34924. * @param string which directory to look for classes, defaults to
  34925. * the Installer/Roles subdirectory of
  34926. * the directory from where this file (__FILE__) is
  34927. * included.
  34928. *
  34929. * @return bool TRUE on success, a PEAR error on failure
  34930. */
  34931. public static function registerRoles($dir = null)
  34932. {
  34933. $GLOBALS['_PEAR_INSTALLER_ROLES'] = array();
  34934. $parser = new PEAR_XMLParser;
  34935. if ($dir === null) {
  34936. $dir = dirname(__FILE__) . '/Role';
  34937. }
  34938. if (!file_exists($dir) || !is_dir($dir)) {
  34939. return PEAR::raiseError("registerRoles: opendir($dir) failed: does not exist/is not directory");
  34940. }
  34941. $dp = @opendir($dir);
  34942. if (empty($dp)) {
  34943. return PEAR::raiseError("registerRoles: opendir($dir) failed: $php_errmsg");
  34944. }
  34945. while ($entry = readdir($dp)) {
  34946. if ($entry[0] == '.' || substr($entry, -4) != '.xml') {
  34947. continue;
  34948. }
  34949. $class = "PEAR_Installer_Role_".substr($entry, 0, -4);
  34950. // List of roles
  34951. if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'][$class])) {
  34952. $file = "$dir/$entry";
  34953. $parser->parse(file_get_contents($file));
  34954. $data = $parser->getData();
  34955. if (!is_array($data['releasetypes'])) {
  34956. $data['releasetypes'] = array($data['releasetypes']);
  34957. }
  34958. $GLOBALS['_PEAR_INSTALLER_ROLES'][$class] = $data;
  34959. }
  34960. }
  34961. closedir($dp);
  34962. ksort($GLOBALS['_PEAR_INSTALLER_ROLES']);
  34963. PEAR_Installer_Role::getBaseinstallRoles(true);
  34964. PEAR_Installer_Role::getInstallableRoles(true);
  34965. PEAR_Installer_Role::getPhpRoles(true);
  34966. PEAR_Installer_Role::getValidRoles('****', true);
  34967. return true;
  34968. }
  34969. }������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/PackageFile/Generator/v1.php������������������������������������������������������0000664�0001750�0001750�00000142261�14720722517�020145� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  34970. /**
  34971. * package.xml generation class, package.xml version 1.0
  34972. *
  34973. * PHP versions 4 and 5
  34974. *
  34975. * @category pear
  34976. * @package PEAR
  34977. * @author Greg Beaver <cellog@php.net>
  34978. * @copyright 1997-2009 The Authors
  34979. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34980. * @link http://pear.php.net/package/PEAR
  34981. * @since File available since Release 1.4.0a1
  34982. */
  34983. /**
  34984. * needed for PEAR_VALIDATE_* constants
  34985. */
  34986. require_once 'PEAR/Validate.php';
  34987. require_once 'System.php';
  34988. require_once 'PEAR/PackageFile/v2.php';
  34989. /**
  34990. * This class converts a PEAR_PackageFile_v1 object into any output format.
  34991. *
  34992. * Supported output formats include array, XML string, and a PEAR_PackageFile_v2
  34993. * object, for converting package.xml 1.0 into package.xml 2.0 with no sweat.
  34994. * @category pear
  34995. * @package PEAR
  34996. * @author Greg Beaver <cellog@php.net>
  34997. * @copyright 1997-2009 The Authors
  34998. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  34999. * @version Release: 1.10.16
  35000. * @link http://pear.php.net/package/PEAR
  35001. * @since Class available since Release 1.4.0a1
  35002. */
  35003. class PEAR_PackageFile_Generator_v1
  35004. {
  35005. /**
  35006. * @var PEAR_PackageFile_v1
  35007. */
  35008. var $_packagefile;
  35009. function __construct(&$packagefile)
  35010. {
  35011. $this->_packagefile = &$packagefile;
  35012. }
  35013. function getPackagerVersion()
  35014. {
  35015. return '1.10.16';
  35016. }
  35017. /**
  35018. * @param PEAR_Packager
  35019. * @param bool if true, a .tgz is written, otherwise a .tar is written
  35020. * @param string|null directory in which to save the .tgz
  35021. * @return string|PEAR_Error location of package or error object
  35022. */
  35023. function toTgz(&$packager, $compress = true, $where = null)
  35024. {
  35025. require_once 'Archive/Tar.php';
  35026. if ($where === null) {
  35027. if (!($where = System::mktemp(array('-d')))) {
  35028. return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: mktemp failed');
  35029. }
  35030. } elseif (!@System::mkDir(array('-p', $where))) {
  35031. return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: "' . $where . '" could' .
  35032. ' not be created');
  35033. }
  35034. if (file_exists($where . DIRECTORY_SEPARATOR . 'package.xml') &&
  35035. !is_file($where . DIRECTORY_SEPARATOR . 'package.xml')) {
  35036. return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: unable to save package.xml as' .
  35037. ' "' . $where . DIRECTORY_SEPARATOR . 'package.xml"');
  35038. }
  35039. if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) {
  35040. return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: invalid package file');
  35041. }
  35042. $pkginfo = $this->_packagefile->getArray();
  35043. $ext = $compress ? '.tgz' : '.tar';
  35044. $pkgver = $pkginfo['package'] . '-' . $pkginfo['version'];
  35045. $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
  35046. if (file_exists(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext) &&
  35047. !is_file(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext)) {
  35048. return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: cannot create tgz file "' .
  35049. getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext . '"');
  35050. }
  35051. if ($pkgfile = $this->_packagefile->getPackageFile()) {
  35052. $pkgdir = dirname(realpath($pkgfile));
  35053. $pkgfile = basename($pkgfile);
  35054. } else {
  35055. return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: package file object must ' .
  35056. 'be created from a real file');
  35057. }
  35058. // {{{ Create the package file list
  35059. $filelist = array();
  35060. $i = 0;
  35061. foreach ($this->_packagefile->getFilelist() as $fname => $atts) {
  35062. $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
  35063. if (!file_exists($file)) {
  35064. return PEAR::raiseError("File does not exist: $fname");
  35065. } else {
  35066. $filelist[$i++] = $file;
  35067. if (!isset($atts['md5sum'])) {
  35068. $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($file));
  35069. }
  35070. $packager->log(2, "Adding file $fname");
  35071. }
  35072. }
  35073. // }}}
  35074. $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true);
  35075. if ($packagexml) {
  35076. $tar = new Archive_Tar($dest_package, $compress);
  35077. $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
  35078. // ----- Creates with the package.xml file
  35079. $ok = $tar->createModify(array($packagexml), '', $where);
  35080. if (PEAR::isError($ok)) {
  35081. return $ok;
  35082. } elseif (!$ok) {
  35083. return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed');
  35084. }
  35085. // ----- Add the content of the package
  35086. if (!$tar->addModify($filelist, $pkgver, $pkgdir)) {
  35087. return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed');
  35088. }
  35089. return $dest_package;
  35090. }
  35091. }
  35092. /**
  35093. * @param string|null directory to place the package.xml in, or null for a temporary dir
  35094. * @param int one of the PEAR_VALIDATE_* constants
  35095. * @param string name of the generated file
  35096. * @param bool if true, then no analysis will be performed on role="php" files
  35097. * @return string|PEAR_Error path to the created file on success
  35098. */
  35099. function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml',
  35100. $nofilechecking = false)
  35101. {
  35102. if (!$this->_packagefile->validate($state, $nofilechecking)) {
  35103. return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: invalid package.xml',
  35104. null, null, null, $this->_packagefile->getValidationWarnings());
  35105. }
  35106. if ($where === null) {
  35107. if (!($where = System::mktemp(array('-d')))) {
  35108. return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: mktemp failed');
  35109. }
  35110. } elseif (!@System::mkDir(array('-p', $where))) {
  35111. return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: "' . $where . '" could' .
  35112. ' not be created');
  35113. }
  35114. $newpkgfile = $where . DIRECTORY_SEPARATOR . $name;
  35115. $np = @fopen($newpkgfile, 'wb');
  35116. if (!$np) {
  35117. return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: unable to save ' .
  35118. "$name as $newpkgfile");
  35119. }
  35120. fwrite($np, $this->toXml($state, true));
  35121. fclose($np);
  35122. return $newpkgfile;
  35123. }
  35124. /**
  35125. * fix both XML encoding to be UTF8, and replace standard XML entities < > " & '
  35126. *
  35127. * @param string $string
  35128. * @return string
  35129. * @access private
  35130. */
  35131. function _fixXmlEncoding($string)
  35132. {
  35133. return strtr($string, array(
  35134. '&' => '&amp;',
  35135. '>' => '&gt;',
  35136. '<' => '&lt;',
  35137. '"' => '&quot;',
  35138. '\'' => '&apos;' ));
  35139. }
  35140. /**
  35141. * Return an XML document based on the package info (as returned
  35142. * by the PEAR_Common::infoFrom* methods).
  35143. *
  35144. * @return string XML data
  35145. */
  35146. function toXml($state = PEAR_VALIDATE_NORMAL, $nofilevalidation = false)
  35147. {
  35148. $this->_packagefile->setDate(date('Y-m-d'));
  35149. if (!$this->_packagefile->validate($state, $nofilevalidation)) {
  35150. return false;
  35151. }
  35152. $pkginfo = $this->_packagefile->getArray();
  35153. static $maint_map = array(
  35154. "handle" => "user",
  35155. "name" => "name",
  35156. "email" => "email",
  35157. "role" => "role",
  35158. );
  35159. $ret = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
  35160. $ret .= "<!DOCTYPE package SYSTEM \"http://pear.php.net/dtd/package-1.0\">\n";
  35161. $ret .= "<package version=\"1.0\" packagerversion=\"1.10.16\">\n" .
  35162. " <name>$pkginfo[package]</name>";
  35163. if (isset($pkginfo['extends'])) {
  35164. $ret .= "\n<extends>$pkginfo[extends]</extends>";
  35165. }
  35166. $ret .=
  35167. "\n <summary>".$this->_fixXmlEncoding($pkginfo['summary'])."</summary>\n" .
  35168. " <description>".trim($this->_fixXmlEncoding($pkginfo['description']))."\n </description>\n" .
  35169. " <maintainers>\n";
  35170. foreach ($pkginfo['maintainers'] as $maint) {
  35171. $ret .= " <maintainer>\n";
  35172. foreach ($maint_map as $idx => $elm) {
  35173. $ret .= " <$elm>";
  35174. $ret .= $this->_fixXmlEncoding($maint[$idx]);
  35175. $ret .= "</$elm>\n";
  35176. }
  35177. $ret .= " </maintainer>\n";
  35178. }
  35179. $ret .= " </maintainers>\n";
  35180. $ret .= $this->_makeReleaseXml($pkginfo, false, $state);
  35181. if (isset($pkginfo['changelog']) && count($pkginfo['changelog']) > 0) {
  35182. $ret .= " <changelog>\n";
  35183. foreach ($pkginfo['changelog'] as $oldrelease) {
  35184. $ret .= $this->_makeReleaseXml($oldrelease, true);
  35185. }
  35186. $ret .= " </changelog>\n";
  35187. }
  35188. $ret .= "</package>\n";
  35189. return $ret;
  35190. }
  35191. // }}}
  35192. // {{{ _makeReleaseXml()
  35193. /**
  35194. * Generate part of an XML description with release information.
  35195. *
  35196. * @param array $pkginfo array with release information
  35197. * @param bool $changelog whether the result will be in a changelog element
  35198. *
  35199. * @return string XML data
  35200. *
  35201. * @access private
  35202. */
  35203. function _makeReleaseXml($pkginfo, $changelog = false, $state = PEAR_VALIDATE_NORMAL)
  35204. {
  35205. // XXX QUOTE ENTITIES IN PCDATA, OR EMBED IN CDATA BLOCKS!!
  35206. $indent = $changelog ? " " : "";
  35207. $ret = "$indent <release>\n";
  35208. if (!empty($pkginfo['version'])) {
  35209. $ret .= "$indent <version>$pkginfo[version]</version>\n";
  35210. }
  35211. if (!empty($pkginfo['release_date'])) {
  35212. $ret .= "$indent <date>$pkginfo[release_date]</date>\n";
  35213. }
  35214. if (!empty($pkginfo['release_license'])) {
  35215. $ret .= "$indent <license>$pkginfo[release_license]</license>\n";
  35216. }
  35217. if (!empty($pkginfo['release_state'])) {
  35218. $ret .= "$indent <state>$pkginfo[release_state]</state>\n";
  35219. }
  35220. if (!empty($pkginfo['release_notes'])) {
  35221. $ret .= "$indent <notes>".trim($this->_fixXmlEncoding($pkginfo['release_notes']))
  35222. ."\n$indent </notes>\n";
  35223. }
  35224. if (!empty($pkginfo['release_warnings'])) {
  35225. $ret .= "$indent <warnings>".$this->_fixXmlEncoding($pkginfo['release_warnings'])."</warnings>\n";
  35226. }
  35227. if (isset($pkginfo['release_deps']) && sizeof($pkginfo['release_deps']) > 0) {
  35228. $ret .= "$indent <deps>\n";
  35229. foreach ($pkginfo['release_deps'] as $dep) {
  35230. $ret .= "$indent <dep type=\"$dep[type]\" rel=\"$dep[rel]\"";
  35231. if (isset($dep['version'])) {
  35232. $ret .= " version=\"$dep[version]\"";
  35233. }
  35234. if (isset($dep['optional'])) {
  35235. $ret .= " optional=\"$dep[optional]\"";
  35236. }
  35237. if (isset($dep['name'])) {
  35238. $ret .= ">$dep[name]</dep>\n";
  35239. } else {
  35240. $ret .= "/>\n";
  35241. }
  35242. }
  35243. $ret .= "$indent </deps>\n";
  35244. }
  35245. if (isset($pkginfo['configure_options'])) {
  35246. $ret .= "$indent <configureoptions>\n";
  35247. foreach ($pkginfo['configure_options'] as $c) {
  35248. $ret .= "$indent <configureoption name=\"".
  35249. $this->_fixXmlEncoding($c['name']) . "\"";
  35250. if (isset($c['default'])) {
  35251. $ret .= " default=\"" . $this->_fixXmlEncoding($c['default']) . "\"";
  35252. }
  35253. $ret .= " prompt=\"" . $this->_fixXmlEncoding($c['prompt']) . "\"";
  35254. $ret .= "/>\n";
  35255. }
  35256. $ret .= "$indent </configureoptions>\n";
  35257. }
  35258. if (isset($pkginfo['provides'])) {
  35259. foreach ($pkginfo['provides'] as $key => $what) {
  35260. $ret .= "$indent <provides type=\"$what[type]\" ";
  35261. $ret .= "name=\"$what[name]\" ";
  35262. if (isset($what['extends'])) {
  35263. $ret .= "extends=\"$what[extends]\" ";
  35264. }
  35265. $ret .= "/>\n";
  35266. }
  35267. }
  35268. if (isset($pkginfo['filelist'])) {
  35269. $ret .= "$indent <filelist>\n";
  35270. if ($state ^ PEAR_VALIDATE_PACKAGING) {
  35271. $ret .= $this->recursiveXmlFilelist($pkginfo['filelist']);
  35272. } else {
  35273. foreach ($pkginfo['filelist'] as $file => $fa) {
  35274. if (!isset($fa['role'])) {
  35275. $fa['role'] = '';
  35276. }
  35277. $ret .= "$indent <file role=\"$fa[role]\"";
  35278. if (isset($fa['baseinstalldir'])) {
  35279. $ret .= ' baseinstalldir="' .
  35280. $this->_fixXmlEncoding($fa['baseinstalldir']) . '"';
  35281. }
  35282. if (isset($fa['md5sum'])) {
  35283. $ret .= " md5sum=\"$fa[md5sum]\"";
  35284. }
  35285. if (isset($fa['platform'])) {
  35286. $ret .= " platform=\"$fa[platform]\"";
  35287. }
  35288. if (!empty($fa['install-as'])) {
  35289. $ret .= ' install-as="' .
  35290. $this->_fixXmlEncoding($fa['install-as']) . '"';
  35291. }
  35292. $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"';
  35293. if (empty($fa['replacements'])) {
  35294. $ret .= "/>\n";
  35295. } else {
  35296. $ret .= ">\n";
  35297. foreach ($fa['replacements'] as $r) {
  35298. $ret .= "$indent <replace";
  35299. foreach ($r as $k => $v) {
  35300. $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"';
  35301. }
  35302. $ret .= "/>\n";
  35303. }
  35304. $ret .= "$indent </file>\n";
  35305. }
  35306. }
  35307. }
  35308. $ret .= "$indent </filelist>\n";
  35309. }
  35310. $ret .= "$indent </release>\n";
  35311. return $ret;
  35312. }
  35313. /**
  35314. * @param array
  35315. * @access protected
  35316. */
  35317. function recursiveXmlFilelist($list)
  35318. {
  35319. $this->_dirs = array();
  35320. foreach ($list as $file => $attributes) {
  35321. $this->_addDir($this->_dirs, explode('/', dirname($file)), $file, $attributes);
  35322. }
  35323. return $this->_formatDir($this->_dirs);
  35324. }
  35325. /**
  35326. * @param array
  35327. * @param array
  35328. * @param string|null
  35329. * @param array|null
  35330. * @access private
  35331. */
  35332. function _addDir(&$dirs, $dir, $file = null, $attributes = null)
  35333. {
  35334. if ($dir == array() || $dir == array('.')) {
  35335. $dirs['files'][basename($file)] = $attributes;
  35336. return;
  35337. }
  35338. $curdir = array_shift($dir);
  35339. if (!isset($dirs['dirs'][$curdir])) {
  35340. $dirs['dirs'][$curdir] = array();
  35341. }
  35342. $this->_addDir($dirs['dirs'][$curdir], $dir, $file, $attributes);
  35343. }
  35344. /**
  35345. * @param array
  35346. * @param string
  35347. * @param string
  35348. * @access private
  35349. */
  35350. function _formatDir($dirs, $indent = '', $curdir = '')
  35351. {
  35352. $ret = '';
  35353. if (!count($dirs)) {
  35354. return '';
  35355. }
  35356. if (isset($dirs['dirs'])) {
  35357. uksort($dirs['dirs'], 'strnatcasecmp');
  35358. foreach ($dirs['dirs'] as $dir => $contents) {
  35359. $usedir = "$curdir/$dir";
  35360. $ret .= "$indent <dir name=\"$dir\">\n";
  35361. $ret .= $this->_formatDir($contents, "$indent ", $usedir);
  35362. $ret .= "$indent </dir> <!-- $usedir -->\n";
  35363. }
  35364. }
  35365. if (isset($dirs['files'])) {
  35366. uksort($dirs['files'], 'strnatcasecmp');
  35367. foreach ($dirs['files'] as $file => $attribs) {
  35368. $ret .= $this->_formatFile($file, $attribs, $indent);
  35369. }
  35370. }
  35371. return $ret;
  35372. }
  35373. /**
  35374. * @param string
  35375. * @param array
  35376. * @param string
  35377. * @access private
  35378. */
  35379. function _formatFile($file, $attributes, $indent)
  35380. {
  35381. $ret = "$indent <file role=\"$attributes[role]\"";
  35382. if (isset($attributes['baseinstalldir'])) {
  35383. $ret .= ' baseinstalldir="' .
  35384. $this->_fixXmlEncoding($attributes['baseinstalldir']) . '"';
  35385. }
  35386. if (isset($attributes['md5sum'])) {
  35387. $ret .= " md5sum=\"$attributes[md5sum]\"";
  35388. }
  35389. if (isset($attributes['platform'])) {
  35390. $ret .= " platform=\"$attributes[platform]\"";
  35391. }
  35392. if (!empty($attributes['install-as'])) {
  35393. $ret .= ' install-as="' .
  35394. $this->_fixXmlEncoding($attributes['install-as']) . '"';
  35395. }
  35396. $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"';
  35397. if (empty($attributes['replacements'])) {
  35398. $ret .= "/>\n";
  35399. } else {
  35400. $ret .= ">\n";
  35401. foreach ($attributes['replacements'] as $r) {
  35402. $ret .= "$indent <replace";
  35403. foreach ($r as $k => $v) {
  35404. $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"';
  35405. }
  35406. $ret .= "/>\n";
  35407. }
  35408. $ret .= "$indent </file>\n";
  35409. }
  35410. return $ret;
  35411. }
  35412. // {{{ _unIndent()
  35413. /**
  35414. * Unindent given string (?)
  35415. *
  35416. * @param string $str The string that has to be unindented.
  35417. * @return string
  35418. * @access private
  35419. */
  35420. function _unIndent($str)
  35421. {
  35422. // remove leading newlines
  35423. $str = preg_replace('/^[\r\n]+/', '', $str);
  35424. // find whitespace at the beginning of the first line
  35425. $indent_len = strspn($str, " \t");
  35426. $indent = substr($str, 0, $indent_len);
  35427. $data = '';
  35428. // remove the same amount of whitespace from following lines
  35429. foreach (explode("\n", $str) as $line) {
  35430. if (substr($line, 0, $indent_len) == $indent) {
  35431. $data .= substr($line, $indent_len) . "\n";
  35432. }
  35433. }
  35434. return $data;
  35435. }
  35436. /**
  35437. * @return array
  35438. */
  35439. function dependenciesToV2()
  35440. {
  35441. $arr = array();
  35442. $this->_convertDependencies2_0($arr);
  35443. return $arr['dependencies'];
  35444. }
  35445. /**
  35446. * Convert a package.xml version 1.0 into version 2.0
  35447. *
  35448. * Note that this does a basic conversion, to allow more advanced
  35449. * features like bundles and multiple releases
  35450. * @param string the classname to instantiate and return. This must be
  35451. * PEAR_PackageFile_v2 or a descendant
  35452. * @param boolean if true, only valid, deterministic package.xml 1.0 as defined by the
  35453. * strictest parameters will be converted
  35454. * @return PEAR_PackageFile_v2|PEAR_Error
  35455. */
  35456. function &toV2($class = 'PEAR_PackageFile_v2', $strict = false)
  35457. {
  35458. if ($strict) {
  35459. if (!$this->_packagefile->validate()) {
  35460. $a = PEAR::raiseError('invalid package.xml version 1.0 cannot be converted' .
  35461. ' to version 2.0', null, null, null,
  35462. $this->_packagefile->getValidationWarnings(true));
  35463. return $a;
  35464. }
  35465. }
  35466. $arr = array(
  35467. 'attribs' => array(
  35468. 'version' => '2.0',
  35469. 'xmlns' => 'http://pear.php.net/dtd/package-2.0',
  35470. 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
  35471. 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
  35472. 'xsi:schemaLocation' => "http://pear.php.net/dtd/tasks-1.0\n" .
  35473. "http://pear.php.net/dtd/tasks-1.0.xsd\n" .
  35474. "http://pear.php.net/dtd/package-2.0\n" .
  35475. 'http://pear.php.net/dtd/package-2.0.xsd',
  35476. ),
  35477. 'name' => $this->_packagefile->getPackage(),
  35478. 'channel' => 'pear.php.net',
  35479. );
  35480. $arr['summary'] = $this->_packagefile->getSummary();
  35481. $arr['description'] = $this->_packagefile->getDescription();
  35482. $maintainers = $this->_packagefile->getMaintainers();
  35483. foreach ($maintainers as $maintainer) {
  35484. if ($maintainer['role'] != 'lead') {
  35485. continue;
  35486. }
  35487. $new = array(
  35488. 'name' => $maintainer['name'],
  35489. 'user' => $maintainer['handle'],
  35490. 'email' => $maintainer['email'],
  35491. 'active' => 'yes',
  35492. );
  35493. $arr['lead'][] = $new;
  35494. }
  35495. if (!isset($arr['lead'])) { // some people... you know?
  35496. $arr['lead'] = array(
  35497. 'name' => 'unknown',
  35498. 'user' => 'unknown',
  35499. 'email' => 'noleadmaintainer@example.com',
  35500. 'active' => 'no',
  35501. );
  35502. }
  35503. if (count($arr['lead']) == 1) {
  35504. $arr['lead'] = $arr['lead'][0];
  35505. }
  35506. foreach ($maintainers as $maintainer) {
  35507. if ($maintainer['role'] == 'lead') {
  35508. continue;
  35509. }
  35510. $new = array(
  35511. 'name' => $maintainer['name'],
  35512. 'user' => $maintainer['handle'],
  35513. 'email' => $maintainer['email'],
  35514. 'active' => 'yes',
  35515. );
  35516. $arr[$maintainer['role']][] = $new;
  35517. }
  35518. if (isset($arr['developer']) && count($arr['developer']) == 1) {
  35519. $arr['developer'] = $arr['developer'][0];
  35520. }
  35521. if (isset($arr['contributor']) && count($arr['contributor']) == 1) {
  35522. $arr['contributor'] = $arr['contributor'][0];
  35523. }
  35524. if (isset($arr['helper']) && count($arr['helper']) == 1) {
  35525. $arr['helper'] = $arr['helper'][0];
  35526. }
  35527. $arr['date'] = $this->_packagefile->getDate();
  35528. $arr['version'] =
  35529. array(
  35530. 'release' => $this->_packagefile->getVersion(),
  35531. 'api' => $this->_packagefile->getVersion(),
  35532. );
  35533. $arr['stability'] =
  35534. array(
  35535. 'release' => $this->_packagefile->getState(),
  35536. 'api' => $this->_packagefile->getState(),
  35537. );
  35538. $licensemap =
  35539. array(
  35540. 'php' => 'http://www.php.net/license',
  35541. 'php license' => 'http://www.php.net/license',
  35542. 'lgpl' => 'http://www.gnu.org/copyleft/lesser.html',
  35543. 'bsd' => 'http://www.opensource.org/licenses/bsd-license.php',
  35544. 'bsd style' => 'http://www.opensource.org/licenses/bsd-license.php',
  35545. 'bsd-style' => 'http://www.opensource.org/licenses/bsd-license.php',
  35546. 'mit' => 'http://www.opensource.org/licenses/mit-license.php',
  35547. 'gpl' => 'http://www.gnu.org/copyleft/gpl.html',
  35548. 'apache' => 'http://www.opensource.org/licenses/apache2.0.php'
  35549. );
  35550. if (isset($licensemap[strtolower($this->_packagefile->getLicense())])) {
  35551. $arr['license'] = array(
  35552. 'attribs' => array('uri' =>
  35553. $licensemap[strtolower($this->_packagefile->getLicense())]),
  35554. '_content' => $this->_packagefile->getLicense()
  35555. );
  35556. } else {
  35557. // don't use bogus uri
  35558. $arr['license'] = $this->_packagefile->getLicense();
  35559. }
  35560. $arr['notes'] = $this->_packagefile->getNotes();
  35561. $temp = array();
  35562. $arr['contents'] = $this->_convertFilelist2_0($temp);
  35563. $this->_convertDependencies2_0($arr);
  35564. $release = ($this->_packagefile->getConfigureOptions() || $this->_isExtension) ?
  35565. 'extsrcrelease' : 'phprelease';
  35566. if ($release == 'extsrcrelease') {
  35567. $arr['channel'] = 'pecl.php.net';
  35568. $arr['providesextension'] = $arr['name']; // assumption
  35569. }
  35570. $arr[$release] = array();
  35571. if ($this->_packagefile->getConfigureOptions()) {
  35572. $arr[$release]['configureoption'] = $this->_packagefile->getConfigureOptions();
  35573. foreach ($arr[$release]['configureoption'] as $i => $opt) {
  35574. $arr[$release]['configureoption'][$i] = array('attribs' => $opt);
  35575. }
  35576. if (count($arr[$release]['configureoption']) == 1) {
  35577. $arr[$release]['configureoption'] = $arr[$release]['configureoption'][0];
  35578. }
  35579. }
  35580. $this->_convertRelease2_0($arr[$release], $temp);
  35581. if ($release == 'extsrcrelease' && count($arr[$release]) > 1) {
  35582. // multiple extsrcrelease tags added in PEAR 1.4.1
  35583. $arr['dependencies']['required']['pearinstaller']['min'] = '1.4.1';
  35584. }
  35585. if ($cl = $this->_packagefile->getChangelog()) {
  35586. foreach ($cl as $release) {
  35587. $rel = array();
  35588. $rel['version'] =
  35589. array(
  35590. 'release' => $release['version'],
  35591. 'api' => $release['version'],
  35592. );
  35593. if (!isset($release['release_state'])) {
  35594. $release['release_state'] = 'stable';
  35595. }
  35596. $rel['stability'] =
  35597. array(
  35598. 'release' => $release['release_state'],
  35599. 'api' => $release['release_state'],
  35600. );
  35601. if (isset($release['release_date'])) {
  35602. $rel['date'] = $release['release_date'];
  35603. } else {
  35604. $rel['date'] = date('Y-m-d');
  35605. }
  35606. if (isset($release['release_license'])) {
  35607. if (isset($licensemap[strtolower($release['release_license'])])) {
  35608. $uri = $licensemap[strtolower($release['release_license'])];
  35609. } else {
  35610. $uri = 'http://www.example.com';
  35611. }
  35612. $rel['license'] = array(
  35613. 'attribs' => array('uri' => $uri),
  35614. '_content' => $release['release_license']
  35615. );
  35616. } else {
  35617. $rel['license'] = $arr['license'];
  35618. }
  35619. if (!isset($release['release_notes'])) {
  35620. $release['release_notes'] = 'no release notes';
  35621. }
  35622. $rel['notes'] = $release['release_notes'];
  35623. $arr['changelog']['release'][] = $rel;
  35624. }
  35625. }
  35626. $ret = new $class;
  35627. $ret->setConfig($this->_packagefile->_config);
  35628. if (isset($this->_packagefile->_logger) && is_object($this->_packagefile->_logger)) {
  35629. $ret->setLogger($this->_packagefile->_logger);
  35630. }
  35631. $ret->fromArray($arr);
  35632. return $ret;
  35633. }
  35634. /**
  35635. * @param array
  35636. * @param bool
  35637. * @access private
  35638. */
  35639. function _convertDependencies2_0(&$release, $internal = false)
  35640. {
  35641. $peardep = array('pearinstaller' =>
  35642. array('min' => '1.4.0b1')); // this is a lot safer
  35643. $required = $optional = array();
  35644. $release['dependencies'] = array('required' => array());
  35645. if ($this->_packagefile->hasDeps()) {
  35646. foreach ($this->_packagefile->getDeps() as $dep) {
  35647. if (!isset($dep['optional']) || $dep['optional'] == 'no') {
  35648. $required[] = $dep;
  35649. } else {
  35650. $optional[] = $dep;
  35651. }
  35652. }
  35653. foreach (array('required', 'optional') as $arr) {
  35654. $deps = array();
  35655. foreach ($$arr as $dep) {
  35656. // organize deps by dependency type and name
  35657. if (!isset($deps[$dep['type']])) {
  35658. $deps[$dep['type']] = array();
  35659. }
  35660. if (isset($dep['name'])) {
  35661. $deps[$dep['type']][$dep['name']][] = $dep;
  35662. } else {
  35663. $deps[$dep['type']][] = $dep;
  35664. }
  35665. }
  35666. do {
  35667. if (isset($deps['php'])) {
  35668. $php = array();
  35669. if (count($deps['php']) > 1) {
  35670. $php = $this->_processPhpDeps($deps['php']);
  35671. } else {
  35672. if (!isset($deps['php'][0])) {
  35673. // Buggy versions
  35674. $key = key($deps['php']);
  35675. $info = current($deps['php']);
  35676. $deps['php'] = array($info[0]);
  35677. }
  35678. $php = $this->_processDep($deps['php'][0]);
  35679. if (!$php) {
  35680. break; // poor mans throw
  35681. }
  35682. }
  35683. $release['dependencies'][$arr]['php'] = $php;
  35684. }
  35685. } while (false);
  35686. do {
  35687. if (isset($deps['pkg'])) {
  35688. $pkg = array();
  35689. $pkg = $this->_processMultipleDepsName($deps['pkg']);
  35690. if (!$pkg) {
  35691. break; // poor mans throw
  35692. }
  35693. $release['dependencies'][$arr]['package'] = $pkg;
  35694. }
  35695. } while (false);
  35696. do {
  35697. if (isset($deps['ext'])) {
  35698. $pkg = array();
  35699. $pkg = $this->_processMultipleDepsName($deps['ext']);
  35700. $release['dependencies'][$arr]['extension'] = $pkg;
  35701. }
  35702. } while (false);
  35703. // skip sapi - it's not supported so nobody will have used it
  35704. // skip os - it's not supported in 1.0
  35705. }
  35706. }
  35707. if (isset($release['dependencies']['required'])) {
  35708. $release['dependencies']['required'] =
  35709. array_merge($peardep, $release['dependencies']['required']);
  35710. } else {
  35711. $release['dependencies']['required'] = $peardep;
  35712. }
  35713. if (!isset($release['dependencies']['required']['php'])) {
  35714. $release['dependencies']['required']['php'] =
  35715. array('min' => '4.0.0');
  35716. }
  35717. $order = array();
  35718. $bewm = $release['dependencies']['required'];
  35719. $order['php'] = $bewm['php'];
  35720. $order['pearinstaller'] = $bewm['pearinstaller'];
  35721. isset($bewm['package']) ? $order['package'] = $bewm['package'] :0;
  35722. isset($bewm['extension']) ? $order['extension'] = $bewm['extension'] :0;
  35723. $release['dependencies']['required'] = $order;
  35724. }
  35725. /**
  35726. * @param array
  35727. * @access private
  35728. */
  35729. function _convertFilelist2_0(&$package)
  35730. {
  35731. $ret = array('dir' =>
  35732. array(
  35733. 'attribs' => array('name' => '/'),
  35734. 'file' => array()
  35735. )
  35736. );
  35737. $package['platform'] =
  35738. $package['install-as'] = array();
  35739. $this->_isExtension = false;
  35740. foreach ($this->_packagefile->getFilelist() as $name => $file) {
  35741. $file['name'] = $name;
  35742. if (isset($file['role']) && $file['role'] == 'src') {
  35743. $this->_isExtension = true;
  35744. }
  35745. if (isset($file['replacements'])) {
  35746. $repl = $file['replacements'];
  35747. unset($file['replacements']);
  35748. } else {
  35749. unset($repl);
  35750. }
  35751. if (isset($file['install-as'])) {
  35752. $package['install-as'][$name] = $file['install-as'];
  35753. unset($file['install-as']);
  35754. }
  35755. if (isset($file['platform'])) {
  35756. $package['platform'][$name] = $file['platform'];
  35757. unset($file['platform']);
  35758. }
  35759. $file = array('attribs' => $file);
  35760. if (isset($repl)) {
  35761. foreach ($repl as $replace ) {
  35762. $file['tasks:replace'][] = array('attribs' => $replace);
  35763. }
  35764. if (count($repl) == 1) {
  35765. $file['tasks:replace'] = $file['tasks:replace'][0];
  35766. }
  35767. }
  35768. $ret['dir']['file'][] = $file;
  35769. }
  35770. return $ret;
  35771. }
  35772. /**
  35773. * Post-process special files with install-as/platform attributes and
  35774. * make the release tag.
  35775. *
  35776. * This complex method follows this work-flow to create the release tags:
  35777. *
  35778. * <pre>
  35779. * - if any install-as/platform exist, create a generic release and fill it with
  35780. * o <install as=..> tags for <file name=... install-as=...>
  35781. * o <install as=..> tags for <file name=... platform=!... install-as=..>
  35782. * o <ignore> tags for <file name=... platform=...>
  35783. * o <ignore> tags for <file name=... platform=... install-as=..>
  35784. * - create a release for each platform encountered and fill with
  35785. * o <install as..> tags for <file name=... install-as=...>
  35786. * o <install as..> tags for <file name=... platform=this platform install-as=..>
  35787. * o <install as..> tags for <file name=... platform=!other platform install-as=..>
  35788. * o <ignore> tags for <file name=... platform=!this platform>
  35789. * o <ignore> tags for <file name=... platform=other platform>
  35790. * o <ignore> tags for <file name=... platform=other platform install-as=..>
  35791. * o <ignore> tags for <file name=... platform=!this platform install-as=..>
  35792. * </pre>
  35793. *
  35794. * It does this by accessing the $package parameter, which contains an array with
  35795. * indices:
  35796. *
  35797. * - platform: mapping of file => OS the file should be installed on
  35798. * - install-as: mapping of file => installed name
  35799. * - osmap: mapping of OS => list of files that should be installed
  35800. * on that OS
  35801. * - notosmap: mapping of OS => list of files that should not be
  35802. * installed on that OS
  35803. *
  35804. * @param array
  35805. * @param array
  35806. * @access private
  35807. */
  35808. function _convertRelease2_0(&$release, $package)
  35809. {
  35810. //- if any install-as/platform exist, create a generic release and fill it with
  35811. if (count($package['platform']) || count($package['install-as'])) {
  35812. $generic = array();
  35813. $genericIgnore = array();
  35814. foreach ($package['install-as'] as $file => $as) {
  35815. //o <install as=..> tags for <file name=... install-as=...>
  35816. if (!isset($package['platform'][$file])) {
  35817. $generic[] = $file;
  35818. continue;
  35819. }
  35820. //o <install as=..> tags for <file name=... platform=!... install-as=..>
  35821. if (isset($package['platform'][$file]) &&
  35822. $package['platform'][$file][0] == '!') {
  35823. $generic[] = $file;
  35824. continue;
  35825. }
  35826. //o <ignore> tags for <file name=... platform=... install-as=..>
  35827. if (isset($package['platform'][$file]) &&
  35828. $package['platform'][$file][0] != '!') {
  35829. $genericIgnore[] = $file;
  35830. continue;
  35831. }
  35832. }
  35833. foreach ($package['platform'] as $file => $platform) {
  35834. if (isset($package['install-as'][$file])) {
  35835. continue;
  35836. }
  35837. if ($platform[0] != '!') {
  35838. //o <ignore> tags for <file name=... platform=...>
  35839. $genericIgnore[] = $file;
  35840. }
  35841. }
  35842. if (count($package['platform'])) {
  35843. $oses = $notplatform = $platform = array();
  35844. foreach ($package['platform'] as $file => $os) {
  35845. // get a list of oses
  35846. if ($os[0] == '!') {
  35847. if (isset($oses[substr($os, 1)])) {
  35848. continue;
  35849. }
  35850. $oses[substr($os, 1)] = count($oses);
  35851. } else {
  35852. if (isset($oses[$os])) {
  35853. continue;
  35854. }
  35855. $oses[$os] = count($oses);
  35856. }
  35857. }
  35858. //- create a release for each platform encountered and fill with
  35859. foreach ($oses as $os => $releaseNum) {
  35860. $release[$releaseNum]['installconditions']['os']['name'] = $os;
  35861. $release[$releaseNum]['filelist'] = array('install' => array(),
  35862. 'ignore' => array());
  35863. foreach ($package['install-as'] as $file => $as) {
  35864. //o <install as=..> tags for <file name=... install-as=...>
  35865. if (!isset($package['platform'][$file])) {
  35866. $release[$releaseNum]['filelist']['install'][] =
  35867. array(
  35868. 'attribs' => array(
  35869. 'name' => $file,
  35870. 'as' => $as,
  35871. ),
  35872. );
  35873. continue;
  35874. }
  35875. //o <install as..> tags for
  35876. // <file name=... platform=this platform install-as=..>
  35877. if (isset($package['platform'][$file]) &&
  35878. $package['platform'][$file] == $os) {
  35879. $release[$releaseNum]['filelist']['install'][] =
  35880. array(
  35881. 'attribs' => array(
  35882. 'name' => $file,
  35883. 'as' => $as,
  35884. ),
  35885. );
  35886. continue;
  35887. }
  35888. //o <install as..> tags for
  35889. // <file name=... platform=!other platform install-as=..>
  35890. if (isset($package['platform'][$file]) &&
  35891. $package['platform'][$file] != "!$os" &&
  35892. $package['platform'][$file][0] == '!') {
  35893. $release[$releaseNum]['filelist']['install'][] =
  35894. array(
  35895. 'attribs' => array(
  35896. 'name' => $file,
  35897. 'as' => $as,
  35898. ),
  35899. );
  35900. continue;
  35901. }
  35902. //o <ignore> tags for
  35903. // <file name=... platform=!this platform install-as=..>
  35904. if (isset($package['platform'][$file]) &&
  35905. $package['platform'][$file] == "!$os") {
  35906. $release[$releaseNum]['filelist']['ignore'][] =
  35907. array(
  35908. 'attribs' => array(
  35909. 'name' => $file,
  35910. ),
  35911. );
  35912. continue;
  35913. }
  35914. //o <ignore> tags for
  35915. // <file name=... platform=other platform install-as=..>
  35916. if (isset($package['platform'][$file]) &&
  35917. $package['platform'][$file][0] != '!' &&
  35918. $package['platform'][$file] != $os) {
  35919. $release[$releaseNum]['filelist']['ignore'][] =
  35920. array(
  35921. 'attribs' => array(
  35922. 'name' => $file,
  35923. ),
  35924. );
  35925. continue;
  35926. }
  35927. }
  35928. foreach ($package['platform'] as $file => $platform) {
  35929. if (isset($package['install-as'][$file])) {
  35930. continue;
  35931. }
  35932. //o <ignore> tags for <file name=... platform=!this platform>
  35933. if ($platform == "!$os") {
  35934. $release[$releaseNum]['filelist']['ignore'][] =
  35935. array(
  35936. 'attribs' => array(
  35937. 'name' => $file,
  35938. ),
  35939. );
  35940. continue;
  35941. }
  35942. //o <ignore> tags for <file name=... platform=other platform>
  35943. if ($platform[0] != '!' && $platform != $os) {
  35944. $release[$releaseNum]['filelist']['ignore'][] =
  35945. array(
  35946. 'attribs' => array(
  35947. 'name' => $file,
  35948. ),
  35949. );
  35950. }
  35951. }
  35952. if (!count($release[$releaseNum]['filelist']['install'])) {
  35953. unset($release[$releaseNum]['filelist']['install']);
  35954. }
  35955. if (!count($release[$releaseNum]['filelist']['ignore'])) {
  35956. unset($release[$releaseNum]['filelist']['ignore']);
  35957. }
  35958. }
  35959. if (count($generic) || count($genericIgnore)) {
  35960. $release[count($oses)] = array();
  35961. if (count($generic)) {
  35962. foreach ($generic as $file) {
  35963. if (isset($package['install-as'][$file])) {
  35964. $installas = $package['install-as'][$file];
  35965. } else {
  35966. $installas = $file;
  35967. }
  35968. $release[count($oses)]['filelist']['install'][] =
  35969. array(
  35970. 'attribs' => array(
  35971. 'name' => $file,
  35972. 'as' => $installas,
  35973. )
  35974. );
  35975. }
  35976. }
  35977. if (count($genericIgnore)) {
  35978. foreach ($genericIgnore as $file) {
  35979. $release[count($oses)]['filelist']['ignore'][] =
  35980. array(
  35981. 'attribs' => array(
  35982. 'name' => $file,
  35983. )
  35984. );
  35985. }
  35986. }
  35987. }
  35988. // cleanup
  35989. foreach ($release as $i => $rel) {
  35990. if (isset($rel['filelist']['install']) &&
  35991. count($rel['filelist']['install']) == 1) {
  35992. $release[$i]['filelist']['install'] =
  35993. $release[$i]['filelist']['install'][0];
  35994. }
  35995. if (isset($rel['filelist']['ignore']) &&
  35996. count($rel['filelist']['ignore']) == 1) {
  35997. $release[$i]['filelist']['ignore'] =
  35998. $release[$i]['filelist']['ignore'][0];
  35999. }
  36000. }
  36001. if (count($release) == 1) {
  36002. $release = $release[0];
  36003. }
  36004. } else {
  36005. // no platform atts, but some install-as atts
  36006. foreach ($package['install-as'] as $file => $value) {
  36007. $release['filelist']['install'][] =
  36008. array(
  36009. 'attribs' => array(
  36010. 'name' => $file,
  36011. 'as' => $value
  36012. )
  36013. );
  36014. }
  36015. if (count($release['filelist']['install']) == 1) {
  36016. $release['filelist']['install'] = $release['filelist']['install'][0];
  36017. }
  36018. }
  36019. }
  36020. }
  36021. /**
  36022. * @param array
  36023. * @return array
  36024. * @access private
  36025. */
  36026. function _processDep($dep)
  36027. {
  36028. if ($dep['type'] == 'php') {
  36029. if ($dep['rel'] == 'has') {
  36030. // come on - everyone has php!
  36031. return false;
  36032. }
  36033. }
  36034. $php = array();
  36035. if ($dep['type'] != 'php') {
  36036. $php['name'] = $dep['name'];
  36037. if ($dep['type'] == 'pkg') {
  36038. $php['channel'] = 'pear.php.net';
  36039. }
  36040. }
  36041. switch ($dep['rel']) {
  36042. case 'gt' :
  36043. $php['min'] = $dep['version'];
  36044. $php['exclude'] = $dep['version'];
  36045. break;
  36046. case 'ge' :
  36047. if (!isset($dep['version'])) {
  36048. if ($dep['type'] == 'php') {
  36049. if (isset($dep['name'])) {
  36050. $dep['version'] = $dep['name'];
  36051. }
  36052. }
  36053. }
  36054. $php['min'] = $dep['version'];
  36055. break;
  36056. case 'lt' :
  36057. $php['max'] = $dep['version'];
  36058. $php['exclude'] = $dep['version'];
  36059. break;
  36060. case 'le' :
  36061. $php['max'] = $dep['version'];
  36062. break;
  36063. case 'eq' :
  36064. $php['min'] = $dep['version'];
  36065. $php['max'] = $dep['version'];
  36066. break;
  36067. case 'ne' :
  36068. $php['exclude'] = $dep['version'];
  36069. break;
  36070. case 'not' :
  36071. $php['conflicts'] = 'yes';
  36072. break;
  36073. }
  36074. return $php;
  36075. }
  36076. /**
  36077. * @param array
  36078. * @return array
  36079. */
  36080. function _processPhpDeps($deps)
  36081. {
  36082. $test = array();
  36083. foreach ($deps as $dep) {
  36084. $test[] = $this->_processDep($dep);
  36085. }
  36086. $min = array();
  36087. $max = array();
  36088. foreach ($test as $dep) {
  36089. if (!$dep) {
  36090. continue;
  36091. }
  36092. if (isset($dep['min'])) {
  36093. $min[$dep['min']] = count($min);
  36094. }
  36095. if (isset($dep['max'])) {
  36096. $max[$dep['max']] = count($max);
  36097. }
  36098. }
  36099. if (count($min) > 0) {
  36100. uksort($min, 'version_compare');
  36101. }
  36102. if (count($max) > 0) {
  36103. uksort($max, 'version_compare');
  36104. }
  36105. if (count($min)) {
  36106. // get the highest minimum
  36107. $a = array_flip($min);
  36108. $min = array_pop($a);
  36109. } else {
  36110. $min = false;
  36111. }
  36112. if (count($max)) {
  36113. // get the lowest maximum
  36114. $a = array_flip($max);
  36115. $max = array_shift($a);
  36116. } else {
  36117. $max = false;
  36118. }
  36119. if ($min) {
  36120. $php['min'] = $min;
  36121. }
  36122. if ($max) {
  36123. $php['max'] = $max;
  36124. }
  36125. $exclude = array();
  36126. foreach ($test as $dep) {
  36127. if (!isset($dep['exclude'])) {
  36128. continue;
  36129. }
  36130. $exclude[] = $dep['exclude'];
  36131. }
  36132. if (count($exclude)) {
  36133. $php['exclude'] = $exclude;
  36134. }
  36135. return $php;
  36136. }
  36137. /**
  36138. * process multiple dependencies that have a name, like package deps
  36139. * @param array
  36140. * @return array
  36141. * @access private
  36142. */
  36143. function _processMultipleDepsName($deps)
  36144. {
  36145. $ret = $tests = array();
  36146. foreach ($deps as $name => $dep) {
  36147. foreach ($dep as $d) {
  36148. $tests[$name][] = $this->_processDep($d);
  36149. }
  36150. }
  36151. foreach ($tests as $name => $test) {
  36152. $max = $min = $php = array();
  36153. $php['name'] = $name;
  36154. foreach ($test as $dep) {
  36155. if (!$dep) {
  36156. continue;
  36157. }
  36158. if (isset($dep['channel'])) {
  36159. $php['channel'] = 'pear.php.net';
  36160. }
  36161. if (isset($dep['conflicts']) && $dep['conflicts'] == 'yes') {
  36162. $php['conflicts'] = 'yes';
  36163. }
  36164. if (isset($dep['min'])) {
  36165. $min[$dep['min']] = count($min);
  36166. }
  36167. if (isset($dep['max'])) {
  36168. $max[$dep['max']] = count($max);
  36169. }
  36170. }
  36171. if (count($min) > 0) {
  36172. uksort($min, 'version_compare');
  36173. }
  36174. if (count($max) > 0) {
  36175. uksort($max, 'version_compare');
  36176. }
  36177. if (count($min)) {
  36178. // get the highest minimum
  36179. $a = array_flip($min);
  36180. $min = array_pop($a);
  36181. } else {
  36182. $min = false;
  36183. }
  36184. if (count($max)) {
  36185. // get the lowest maximum
  36186. $a = array_flip($max);
  36187. $max = array_shift($a);
  36188. } else {
  36189. $max = false;
  36190. }
  36191. if ($min) {
  36192. $php['min'] = $min;
  36193. }
  36194. if ($max) {
  36195. $php['max'] = $max;
  36196. }
  36197. $exclude = array();
  36198. foreach ($test as $dep) {
  36199. if (!isset($dep['exclude'])) {
  36200. continue;
  36201. }
  36202. $exclude[] = $dep['exclude'];
  36203. }
  36204. if (count($exclude)) {
  36205. $php['exclude'] = $exclude;
  36206. }
  36207. $ret[] = $php;
  36208. }
  36209. return $ret;
  36210. }
  36211. }
  36212. ?>
  36213. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/PackageFile/Generator/v2.php������������������������������������������������������0000664�0001750�0001750�00000100711�14720722517�020140� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  36214. /**
  36215. * package.xml generation class, package.xml version 2.0
  36216. *
  36217. * PHP versions 4 and 5
  36218. *
  36219. * @category pear
  36220. * @package PEAR
  36221. * @author Greg Beaver <cellog@php.net>
  36222. * @author Stephan Schmidt (original XML_Serializer code)
  36223. * @copyright 1997-2009 The Authors
  36224. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  36225. * @link http://pear.php.net/package/PEAR
  36226. * @since File available since Release 1.4.0a1
  36227. */
  36228. /**
  36229. * file/dir manipulation routines
  36230. */
  36231. require_once 'System.php';
  36232. require_once 'XML/Util.php';
  36233. /**
  36234. * This class converts a PEAR_PackageFile_v2 object into any output format.
  36235. *
  36236. * Supported output formats include array, XML string (using S. Schmidt's
  36237. * XML_Serializer, slightly customized)
  36238. * @category pear
  36239. * @package PEAR
  36240. * @author Greg Beaver <cellog@php.net>
  36241. * @author Stephan Schmidt (original XML_Serializer code)
  36242. * @copyright 1997-2009 The Authors
  36243. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  36244. * @version Release: 1.10.16
  36245. * @link http://pear.php.net/package/PEAR
  36246. * @since Class available since Release 1.4.0a1
  36247. */
  36248. class PEAR_PackageFile_Generator_v2
  36249. {
  36250. /**
  36251. * default options for the serialization
  36252. * @access private
  36253. * @var array $_defaultOptions
  36254. */
  36255. var $_defaultOptions = array(
  36256. 'indent' => ' ', // string used for indentation
  36257. 'linebreak' => "\n", // string used for newlines
  36258. 'typeHints' => false, // automatically add type hin attributes
  36259. 'addDecl' => true, // add an XML declaration
  36260. 'defaultTagName' => 'XML_Serializer_Tag', // tag used for indexed arrays or invalid names
  36261. 'classAsTagName' => false, // use classname for objects in indexed arrays
  36262. 'keyAttribute' => '_originalKey', // attribute where original key is stored
  36263. 'typeAttribute' => '_type', // attribute for type (only if typeHints => true)
  36264. 'classAttribute' => '_class', // attribute for class of objects (only if typeHints => true)
  36265. 'scalarAsAttributes' => false, // scalar values (strings, ints,..) will be serialized as attribute
  36266. 'prependAttributes' => '', // prepend string for attributes
  36267. 'indentAttributes' => false, // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column
  36268. 'mode' => 'simplexml', // use 'simplexml' to use parent name as tagname if transforming an indexed array
  36269. 'addDoctype' => false, // add a doctype declaration
  36270. 'doctype' => null, // supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()}
  36271. 'rootName' => 'package', // name of the root tag
  36272. 'rootAttributes' => array(
  36273. 'version' => '2.0',
  36274. 'xmlns' => 'http://pear.php.net/dtd/package-2.0',
  36275. 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
  36276. 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
  36277. 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
  36278. http://pear.php.net/dtd/tasks-1.0.xsd
  36279. http://pear.php.net/dtd/package-2.0
  36280. http://pear.php.net/dtd/package-2.0.xsd',
  36281. ), // attributes of the root tag
  36282. 'attributesArray' => 'attribs', // all values in this key will be treated as attributes
  36283. 'contentName' => '_content', // this value will be used directly as content, instead of creating a new tag, may only be used in conjunction with attributesArray
  36284. 'beautifyFilelist' => false,
  36285. 'encoding' => 'UTF-8',
  36286. );
  36287. /**
  36288. * options for the serialization
  36289. * @access private
  36290. * @var array $options
  36291. */
  36292. var $options = array();
  36293. /**
  36294. * current tag depth
  36295. * @var integer $_tagDepth
  36296. */
  36297. var $_tagDepth = 0;
  36298. /**
  36299. * serilialized representation of the data
  36300. * @var string $_serializedData
  36301. */
  36302. var $_serializedData = null;
  36303. /**
  36304. * @var PEAR_PackageFile_v2
  36305. */
  36306. var $_packagefile;
  36307. /**
  36308. * @param PEAR_PackageFile_v2
  36309. */
  36310. function __construct(&$packagefile)
  36311. {
  36312. $this->_packagefile = &$packagefile;
  36313. if (isset($this->_packagefile->encoding)) {
  36314. $this->_defaultOptions['encoding'] = $this->_packagefile->encoding;
  36315. }
  36316. }
  36317. /**
  36318. * @return string
  36319. */
  36320. function getPackagerVersion()
  36321. {
  36322. return '1.10.16';
  36323. }
  36324. /**
  36325. * @param PEAR_Packager
  36326. * @param bool generate a .tgz or a .tar
  36327. * @param string|null temporary directory to package in
  36328. */
  36329. function toTgz(&$packager, $compress = true, $where = null)
  36330. {
  36331. $a = null;
  36332. return $this->toTgz2($packager, $a, $compress, $where);
  36333. }
  36334. /**
  36335. * Package up both a package.xml and package2.xml for the same release
  36336. * @param PEAR_Packager
  36337. * @param PEAR_PackageFile_v1
  36338. * @param bool generate a .tgz or a .tar
  36339. * @param string|null temporary directory to package in
  36340. */
  36341. function toTgz2(&$packager, &$pf1, $compress = true, $where = null)
  36342. {
  36343. require_once 'Archive/Tar.php';
  36344. if (!$this->_packagefile->isEquivalent($pf1)) {
  36345. return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' .
  36346. basename($pf1->getPackageFile()) .
  36347. '" is not equivalent to "' . basename($this->_packagefile->getPackageFile())
  36348. . '"');
  36349. }
  36350. if ($where === null) {
  36351. if (!($where = System::mktemp(array('-d')))) {
  36352. return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: mktemp failed');
  36353. }
  36354. } elseif (!@System::mkDir(array('-p', $where))) {
  36355. return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' . $where . '" could' .
  36356. ' not be created');
  36357. }
  36358. $file = $where . DIRECTORY_SEPARATOR . 'package.xml';
  36359. if (file_exists($file) && !is_file($file)) {
  36360. return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: unable to save package.xml as' .
  36361. ' "' . $file .'"');
  36362. }
  36363. if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) {
  36364. return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: invalid package.xml');
  36365. }
  36366. $ext = $compress ? '.tgz' : '.tar';
  36367. $pkgver = $this->_packagefile->getPackage() . '-' . $this->_packagefile->getVersion();
  36368. $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
  36369. if (file_exists($dest_package) && !is_file($dest_package)) {
  36370. return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: cannot create tgz file "' .
  36371. $dest_package . '"');
  36372. }
  36373. $pkgfile = $this->_packagefile->getPackageFile();
  36374. if (!$pkgfile) {
  36375. return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: package file object must ' .
  36376. 'be created from a real file');
  36377. }
  36378. $pkgdir = dirname(realpath($pkgfile));
  36379. $pkgfile = basename($pkgfile);
  36380. // {{{ Create the package file list
  36381. $filelist = array();
  36382. $i = 0;
  36383. $this->_packagefile->flattenFilelist();
  36384. $contents = $this->_packagefile->getContents();
  36385. if (isset($contents['bundledpackage'])) { // bundles of packages
  36386. $contents = $contents['bundledpackage'];
  36387. if (!isset($contents[0])) {
  36388. $contents = array($contents);
  36389. }
  36390. $packageDir = $where;
  36391. foreach ($contents as $i => $package) {
  36392. $fname = $package;
  36393. $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
  36394. if (!file_exists($file)) {
  36395. return $packager->raiseError("File does not exist: $fname");
  36396. }
  36397. $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname;
  36398. System::mkdir(array('-p', dirname($tfile)));
  36399. copy($file, $tfile);
  36400. $filelist[$i++] = $tfile;
  36401. $packager->log(2, "Adding package $fname");
  36402. }
  36403. } else { // normal packages
  36404. $contents = $contents['dir']['file'];
  36405. if (!isset($contents[0])) {
  36406. $contents = array($contents);
  36407. }
  36408. $packageDir = $where;
  36409. foreach ($contents as $i => $file) {
  36410. $fname = $file['attribs']['name'];
  36411. $atts = $file['attribs'];
  36412. $orig = $file;
  36413. $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
  36414. if (!file_exists($file)) {
  36415. return $packager->raiseError("File does not exist: $fname");
  36416. }
  36417. $origperms = fileperms($file);
  36418. $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname;
  36419. unset($orig['attribs']);
  36420. if (count($orig)) { // file with tasks
  36421. // run any package-time tasks
  36422. $contents = file_get_contents($file);
  36423. foreach ($orig as $tag => $raw) {
  36424. $tag = str_replace(
  36425. array($this->_packagefile->getTasksNs() . ':', '-'),
  36426. array('', '_'), $tag);
  36427. $task = "PEAR_Task_$tag";
  36428. $task = new $task($this->_packagefile->_config,
  36429. $this->_packagefile->_logger,
  36430. PEAR_TASK_PACKAGE);
  36431. $task->init($raw, $atts, null);
  36432. $res = $task->startSession($this->_packagefile, $contents, $tfile);
  36433. if (!$res) {
  36434. continue; // skip this task
  36435. }
  36436. if (PEAR::isError($res)) {
  36437. return $res;
  36438. }
  36439. $contents = $res; // save changes
  36440. System::mkdir(array('-p', dirname($tfile)));
  36441. $wp = fopen($tfile, "wb");
  36442. fwrite($wp, $contents);
  36443. fclose($wp);
  36444. }
  36445. }
  36446. if (!file_exists($tfile)) {
  36447. System::mkdir(array('-p', dirname($tfile)));
  36448. copy($file, $tfile);
  36449. }
  36450. chmod($tfile, $origperms);
  36451. $filelist[$i++] = $tfile;
  36452. $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($tfile), $i - 1);
  36453. $packager->log(2, "Adding file $fname");
  36454. }
  36455. }
  36456. // }}}
  36457. $name = $pf1 !== null ? 'package2.xml' : 'package.xml';
  36458. $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, $name);
  36459. if ($packagexml) {
  36460. $tar = new Archive_Tar($dest_package, $compress);
  36461. $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
  36462. // ----- Creates with the package.xml file
  36463. $ok = $tar->createModify(array($packagexml), '', $where);
  36464. if (PEAR::isError($ok)) {
  36465. return $packager->raiseError($ok);
  36466. } elseif (!$ok) {
  36467. return $packager->raiseError('PEAR_Packagefile_v2::toTgz(): adding ' . $name .
  36468. ' failed');
  36469. }
  36470. // ----- Add the content of the package
  36471. if (!$tar->addModify($filelist, $pkgver, $where)) {
  36472. return $packager->raiseError(
  36473. 'PEAR_Packagefile_v2::toTgz(): tarball creation failed');
  36474. }
  36475. // add the package.xml version 1.0
  36476. if ($pf1 !== null) {
  36477. $pfgen = &$pf1->getDefaultGenerator();
  36478. $packagexml1 = $pfgen->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true);
  36479. if (!$tar->addModify(array($packagexml1), '', $where)) {
  36480. return $packager->raiseError(
  36481. 'PEAR_Packagefile_v2::toTgz(): adding package.xml failed');
  36482. }
  36483. }
  36484. return $dest_package;
  36485. }
  36486. }
  36487. function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml')
  36488. {
  36489. if (!$this->_packagefile->validate($state)) {
  36490. return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: invalid package.xml',
  36491. null, null, null, $this->_packagefile->getValidationWarnings());
  36492. }
  36493. if ($where === null) {
  36494. if (!($where = System::mktemp(array('-d')))) {
  36495. return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: mktemp failed');
  36496. }
  36497. } elseif (!@System::mkDir(array('-p', $where))) {
  36498. return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: "' . $where . '" could' .
  36499. ' not be created');
  36500. }
  36501. $newpkgfile = $where . DIRECTORY_SEPARATOR . $name;
  36502. $np = @fopen($newpkgfile, 'wb');
  36503. if (!$np) {
  36504. return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: unable to save ' .
  36505. "$name as $newpkgfile");
  36506. }
  36507. fwrite($np, $this->toXml($state));
  36508. fclose($np);
  36509. return $newpkgfile;
  36510. }
  36511. function &toV2()
  36512. {
  36513. return $this->_packagefile;
  36514. }
  36515. /**
  36516. * Return an XML document based on the package info (as returned
  36517. * by the PEAR_Common::infoFrom* methods).
  36518. *
  36519. * @return string XML data
  36520. */
  36521. function toXml($state = PEAR_VALIDATE_NORMAL, $options = array())
  36522. {
  36523. $this->_packagefile->setDate(date('Y-m-d'));
  36524. $this->_packagefile->setTime(date('H:i:s'));
  36525. if (!$this->_packagefile->validate($state)) {
  36526. return false;
  36527. }
  36528. if (is_array($options)) {
  36529. $this->options = array_merge($this->_defaultOptions, $options);
  36530. } else {
  36531. $this->options = $this->_defaultOptions;
  36532. }
  36533. $arr = $this->_packagefile->getArray();
  36534. if (isset($arr['filelist'])) {
  36535. unset($arr['filelist']);
  36536. }
  36537. if (isset($arr['_lastversion'])) {
  36538. unset($arr['_lastversion']);
  36539. }
  36540. // Fix the notes a little bit
  36541. if (isset($arr['notes'])) {
  36542. // This trims out the indenting, needs fixing
  36543. $arr['notes'] = "\n" . trim($arr['notes']) . "\n";
  36544. }
  36545. if (isset($arr['changelog']) && !empty($arr['changelog'])) {
  36546. // Fix for inconsistency how the array is filled depending on the changelog release amount
  36547. if (!isset($arr['changelog']['release'][0])) {
  36548. $release = $arr['changelog']['release'];
  36549. unset($arr['changelog']['release']);
  36550. $arr['changelog']['release'] = array();
  36551. $arr['changelog']['release'][0] = $release;
  36552. }
  36553. foreach (array_keys($arr['changelog']['release']) as $key) {
  36554. $c =& $arr['changelog']['release'][$key];
  36555. if (isset($c['notes'])) {
  36556. // This trims out the indenting, needs fixing
  36557. $c['notes'] = "\n" . trim($c['notes']) . "\n";
  36558. }
  36559. }
  36560. }
  36561. if ($state ^ PEAR_VALIDATE_PACKAGING && !isset($arr['bundle'])) {
  36562. $use = $this->_recursiveXmlFilelist($arr['contents']['dir']['file']);
  36563. unset($arr['contents']['dir']['file']);
  36564. if (isset($use['dir'])) {
  36565. $arr['contents']['dir']['dir'] = $use['dir'];
  36566. }
  36567. if (isset($use['file'])) {
  36568. $arr['contents']['dir']['file'] = $use['file'];
  36569. }
  36570. $this->options['beautifyFilelist'] = true;
  36571. }
  36572. $arr['attribs']['packagerversion'] = '1.10.16';
  36573. if ($this->serialize($arr, $options)) {
  36574. return $this->_serializedData . "\n";
  36575. }
  36576. return false;
  36577. }
  36578. function _recursiveXmlFilelist($list)
  36579. {
  36580. $dirs = array();
  36581. if (isset($list['attribs'])) {
  36582. $file = $list['attribs']['name'];
  36583. unset($list['attribs']['name']);
  36584. $attributes = $list['attribs'];
  36585. $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes);
  36586. } else {
  36587. foreach ($list as $a) {
  36588. $file = $a['attribs']['name'];
  36589. $attributes = $a['attribs'];
  36590. unset($a['attribs']);
  36591. $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes, $a);
  36592. }
  36593. }
  36594. $this->_formatDir($dirs);
  36595. $this->_deFormat($dirs);
  36596. return $dirs;
  36597. }
  36598. function _addDir(&$dirs, $dir, $file = null, $attributes = null, $tasks = null)
  36599. {
  36600. if (!$tasks) {
  36601. $tasks = array();
  36602. }
  36603. if ($dir == array() || $dir == array('.')) {
  36604. $dirs['file'][basename($file)] = $tasks;
  36605. $attributes['name'] = basename($file);
  36606. $dirs['file'][basename($file)]['attribs'] = $attributes;
  36607. return;
  36608. }
  36609. $curdir = array_shift($dir);
  36610. if (!isset($dirs['dir'][$curdir])) {
  36611. $dirs['dir'][$curdir] = array();
  36612. }
  36613. $this->_addDir($dirs['dir'][$curdir], $dir, $file, $attributes, $tasks);
  36614. }
  36615. function _formatDir(&$dirs)
  36616. {
  36617. if (!count($dirs)) {
  36618. return array();
  36619. }
  36620. $newdirs = array();
  36621. if (isset($dirs['dir'])) {
  36622. $newdirs['dir'] = $dirs['dir'];
  36623. }
  36624. if (isset($dirs['file'])) {
  36625. $newdirs['file'] = $dirs['file'];
  36626. }
  36627. $dirs = $newdirs;
  36628. if (isset($dirs['dir'])) {
  36629. uksort($dirs['dir'], 'strnatcasecmp');
  36630. foreach ($dirs['dir'] as $dir => $contents) {
  36631. $this->_formatDir($dirs['dir'][$dir]);
  36632. }
  36633. }
  36634. if (isset($dirs['file'])) {
  36635. uksort($dirs['file'], 'strnatcasecmp');
  36636. };
  36637. }
  36638. function _deFormat(&$dirs)
  36639. {
  36640. if (!count($dirs)) {
  36641. return array();
  36642. }
  36643. $newdirs = array();
  36644. if (isset($dirs['dir'])) {
  36645. foreach ($dirs['dir'] as $dir => $contents) {
  36646. $newdir = array();
  36647. $newdir['attribs']['name'] = $dir;
  36648. $this->_deFormat($contents);
  36649. foreach ($contents as $tag => $val) {
  36650. $newdir[$tag] = $val;
  36651. }
  36652. $newdirs['dir'][] = $newdir;
  36653. }
  36654. if (count($newdirs['dir']) == 1) {
  36655. $newdirs['dir'] = $newdirs['dir'][0];
  36656. }
  36657. }
  36658. if (isset($dirs['file'])) {
  36659. foreach ($dirs['file'] as $name => $file) {
  36660. $newdirs['file'][] = $file;
  36661. }
  36662. if (count($newdirs['file']) == 1) {
  36663. $newdirs['file'] = $newdirs['file'][0];
  36664. }
  36665. }
  36666. $dirs = $newdirs;
  36667. }
  36668. /**
  36669. * reset all options to default options
  36670. *
  36671. * @access public
  36672. * @see setOption(), XML_Unserializer()
  36673. */
  36674. function resetOptions()
  36675. {
  36676. $this->options = $this->_defaultOptions;
  36677. }
  36678. /**
  36679. * set an option
  36680. *
  36681. * You can use this method if you do not want to set all options in the constructor
  36682. *
  36683. * @access public
  36684. * @see resetOption(), XML_Serializer()
  36685. */
  36686. function setOption($name, $value)
  36687. {
  36688. $this->options[$name] = $value;
  36689. }
  36690. /**
  36691. * sets several options at once
  36692. *
  36693. * You can use this method if you do not want to set all options in the constructor
  36694. *
  36695. * @access public
  36696. * @see resetOption(), XML_Unserializer(), setOption()
  36697. */
  36698. function setOptions($options)
  36699. {
  36700. $this->options = array_merge($this->options, $options);
  36701. }
  36702. /**
  36703. * serialize data
  36704. *
  36705. * @access public
  36706. * @param mixed $data data to serialize
  36707. * @return boolean true on success, pear error on failure
  36708. */
  36709. function serialize($data, $options = null)
  36710. {
  36711. // if options have been specified, use them instead
  36712. // of the previously defined ones
  36713. if (is_array($options)) {
  36714. $optionsBak = $this->options;
  36715. if (isset($options['overrideOptions']) && $options['overrideOptions'] == true) {
  36716. $this->options = array_merge($this->_defaultOptions, $options);
  36717. } else {
  36718. $this->options = array_merge($this->options, $options);
  36719. }
  36720. } else {
  36721. $optionsBak = null;
  36722. }
  36723. // start depth is zero
  36724. $this->_tagDepth = 0;
  36725. $this->_serializedData = '';
  36726. // serialize an array
  36727. if (is_array($data)) {
  36728. $tagName = isset($this->options['rootName']) ? $this->options['rootName'] : 'array';
  36729. $this->_serializedData .= $this->_serializeArray($data, $tagName, $this->options['rootAttributes']);
  36730. }
  36731. // add doctype declaration
  36732. if ($this->options['addDoctype'] === true) {
  36733. $this->_serializedData = XML_Util::getDoctypeDeclaration($tagName, $this->options['doctype'])
  36734. . $this->options['linebreak']
  36735. . $this->_serializedData;
  36736. }
  36737. // build xml declaration
  36738. if ($this->options['addDecl']) {
  36739. $atts = array();
  36740. $encoding = isset($this->options['encoding']) ? $this->options['encoding'] : null;
  36741. $this->_serializedData = XML_Util::getXMLDeclaration('1.0', $encoding)
  36742. . $this->options['linebreak']
  36743. . $this->_serializedData;
  36744. }
  36745. if ($optionsBak !== null) {
  36746. $this->options = $optionsBak;
  36747. }
  36748. return true;
  36749. }
  36750. /**
  36751. * get the result of the serialization
  36752. *
  36753. * @access public
  36754. * @return string serialized XML
  36755. */
  36756. function getSerializedData()
  36757. {
  36758. if ($this->_serializedData === null) {
  36759. return $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.', XML_SERIALIZER_ERROR_NO_SERIALIZATION);
  36760. }
  36761. return $this->_serializedData;
  36762. }
  36763. /**
  36764. * serialize any value
  36765. *
  36766. * This method checks for the type of the value and calls the appropriate method
  36767. *
  36768. * @access private
  36769. * @param mixed $value
  36770. * @param string $tagName
  36771. * @param array $attributes
  36772. * @return string
  36773. */
  36774. function _serializeValue($value, $tagName = null, $attributes = array())
  36775. {
  36776. if (is_array($value)) {
  36777. $xml = $this->_serializeArray($value, $tagName, $attributes);
  36778. } elseif (is_object($value)) {
  36779. $xml = $this->_serializeObject($value, $tagName);
  36780. } else {
  36781. $tag = array(
  36782. 'qname' => $tagName,
  36783. 'attributes' => $attributes,
  36784. 'content' => $value
  36785. );
  36786. $xml = $this->_createXMLTag($tag);
  36787. }
  36788. return $xml;
  36789. }
  36790. /**
  36791. * serialize an array
  36792. *
  36793. * @access private
  36794. * @param array $array array to serialize
  36795. * @param string $tagName name of the root tag
  36796. * @param array $attributes attributes for the root tag
  36797. * @return string $string serialized data
  36798. * @uses XML_Util::isValidName() to check, whether key has to be substituted
  36799. */
  36800. function _serializeArray(&$array, $tagName = null, $attributes = array())
  36801. {
  36802. $_content = null;
  36803. /**
  36804. * check for special attributes
  36805. */
  36806. if ($this->options['attributesArray'] !== null) {
  36807. if (isset($array[$this->options['attributesArray']])) {
  36808. $attributes = $array[$this->options['attributesArray']];
  36809. unset($array[$this->options['attributesArray']]);
  36810. }
  36811. /**
  36812. * check for special content
  36813. */
  36814. if ($this->options['contentName'] !== null) {
  36815. if (isset($array[$this->options['contentName']])) {
  36816. $_content = $array[$this->options['contentName']];
  36817. unset($array[$this->options['contentName']]);
  36818. }
  36819. }
  36820. }
  36821. /*
  36822. * if mode is set to simpleXML, check whether
  36823. * the array is associative or indexed
  36824. */
  36825. if (is_array($array) && $this->options['mode'] == 'simplexml') {
  36826. $indexed = true;
  36827. if (!count($array)) {
  36828. $indexed = false;
  36829. }
  36830. foreach ($array as $key => $val) {
  36831. if (!is_int($key)) {
  36832. $indexed = false;
  36833. break;
  36834. }
  36835. }
  36836. if ($indexed && $this->options['mode'] == 'simplexml') {
  36837. $string = '';
  36838. foreach ($array as $key => $val) {
  36839. if ($this->options['beautifyFilelist'] && $tagName == 'dir') {
  36840. if (!isset($this->_curdir)) {
  36841. $this->_curdir = '';
  36842. }
  36843. $savedir = $this->_curdir;
  36844. if (isset($val['attribs'])) {
  36845. if ($val['attribs']['name'] == '/') {
  36846. $this->_curdir = '/';
  36847. } else {
  36848. if ($this->_curdir == '/') {
  36849. $this->_curdir = '';
  36850. }
  36851. $this->_curdir .= '/' . $val['attribs']['name'];
  36852. }
  36853. }
  36854. }
  36855. $string .= $this->_serializeValue( $val, $tagName, $attributes);
  36856. if ($this->options['beautifyFilelist'] && $tagName == 'dir') {
  36857. $string .= ' <!-- ' . $this->_curdir . ' -->';
  36858. if (empty($savedir)) {
  36859. unset($this->_curdir);
  36860. } else {
  36861. $this->_curdir = $savedir;
  36862. }
  36863. }
  36864. $string .= $this->options['linebreak'];
  36865. // do indentation
  36866. if ($this->options['indent'] !== null && $this->_tagDepth > 0) {
  36867. $string .= str_repeat($this->options['indent'], $this->_tagDepth);
  36868. }
  36869. }
  36870. return rtrim($string);
  36871. }
  36872. }
  36873. if ($this->options['scalarAsAttributes'] === true) {
  36874. foreach ($array as $key => $value) {
  36875. if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
  36876. unset($array[$key]);
  36877. $attributes[$this->options['prependAttributes'].$key] = $value;
  36878. }
  36879. }
  36880. }
  36881. // check for empty array => create empty tag
  36882. if (empty($array)) {
  36883. $tag = array(
  36884. 'qname' => $tagName,
  36885. 'content' => $_content,
  36886. 'attributes' => $attributes
  36887. );
  36888. } else {
  36889. $this->_tagDepth++;
  36890. $tmp = $this->options['linebreak'];
  36891. foreach ($array as $key => $value) {
  36892. // do indentation
  36893. if ($this->options['indent'] !== null && $this->_tagDepth > 0) {
  36894. $tmp .= str_repeat($this->options['indent'], $this->_tagDepth);
  36895. }
  36896. // copy key
  36897. $origKey = $key;
  36898. // key cannot be used as tagname => use default tag
  36899. $valid = XML_Util::isValidName($key);
  36900. if (PEAR::isError($valid)) {
  36901. if ($this->options['classAsTagName'] && is_object($value)) {
  36902. $key = get_class($value);
  36903. } else {
  36904. $key = $this->options['defaultTagName'];
  36905. }
  36906. }
  36907. $atts = array();
  36908. if ($this->options['typeHints'] === true) {
  36909. $atts[$this->options['typeAttribute']] = gettype($value);
  36910. if ($key !== $origKey) {
  36911. $atts[$this->options['keyAttribute']] = (string)$origKey;
  36912. }
  36913. }
  36914. if ($this->options['beautifyFilelist'] && $key == 'dir') {
  36915. if (!isset($this->_curdir)) {
  36916. $this->_curdir = '';
  36917. }
  36918. $savedir = $this->_curdir;
  36919. if (isset($value['attribs'])) {
  36920. if ($value['attribs']['name'] == '/') {
  36921. $this->_curdir = '/';
  36922. } else {
  36923. $this->_curdir .= '/' . $value['attribs']['name'];
  36924. }
  36925. }
  36926. }
  36927. if (is_string($value) && $value && ($value[strlen($value) - 1] == "\n")) {
  36928. $value .= str_repeat($this->options['indent'], $this->_tagDepth);
  36929. }
  36930. $tmp .= $this->_createXMLTag(array(
  36931. 'qname' => $key,
  36932. 'attributes' => $atts,
  36933. 'content' => $value )
  36934. );
  36935. if ($this->options['beautifyFilelist'] && $key == 'dir') {
  36936. if (isset($value['attribs'])) {
  36937. $tmp .= ' <!-- ' . $this->_curdir . ' -->';
  36938. if (empty($savedir)) {
  36939. unset($this->_curdir);
  36940. } else {
  36941. $this->_curdir = $savedir;
  36942. }
  36943. }
  36944. }
  36945. $tmp .= $this->options['linebreak'];
  36946. }
  36947. $this->_tagDepth--;
  36948. if ($this->options['indent']!==null && $this->_tagDepth>0) {
  36949. $tmp .= str_repeat($this->options['indent'], $this->_tagDepth);
  36950. }
  36951. if (trim($tmp) === '') {
  36952. $tmp = null;
  36953. }
  36954. $tag = array(
  36955. 'qname' => $tagName,
  36956. 'content' => $tmp,
  36957. 'attributes' => $attributes
  36958. );
  36959. }
  36960. if ($this->options['typeHints'] === true) {
  36961. if (!isset($tag['attributes'][$this->options['typeAttribute']])) {
  36962. $tag['attributes'][$this->options['typeAttribute']] = 'array';
  36963. }
  36964. }
  36965. $string = $this->_createXMLTag($tag, false);
  36966. return $string;
  36967. }
  36968. /**
  36969. * create a tag from an array
  36970. * this method awaits an array in the following format
  36971. * array(
  36972. * 'qname' => $tagName,
  36973. * 'attributes' => array(),
  36974. * 'content' => $content, // optional
  36975. * 'namespace' => $namespace // optional
  36976. * 'namespaceUri' => $namespaceUri // optional
  36977. * )
  36978. *
  36979. * @access private
  36980. * @param array $tag tag definition
  36981. * @param boolean $replaceEntities whether to replace XML entities in content or not
  36982. * @return string $string XML tag
  36983. */
  36984. function _createXMLTag($tag, $replaceEntities = true)
  36985. {
  36986. if ($this->options['indentAttributes'] !== false) {
  36987. $multiline = true;
  36988. $indent = str_repeat($this->options['indent'], $this->_tagDepth);
  36989. if ($this->options['indentAttributes'] == '_auto') {
  36990. $indent .= str_repeat(' ', (strlen($tag['qname'])+2));
  36991. } else {
  36992. $indent .= $this->options['indentAttributes'];
  36993. }
  36994. } else {
  36995. $indent = $multiline = false;
  36996. }
  36997. if (is_array($tag['content'])) {
  36998. if (empty($tag['content'])) {
  36999. $tag['content'] = '';
  37000. }
  37001. } elseif(is_scalar($tag['content']) && (string)$tag['content'] == '') {
  37002. $tag['content'] = '';
  37003. }
  37004. if (is_scalar($tag['content']) || is_null($tag['content'])) {
  37005. if ($replaceEntities === true) {
  37006. $replaceEntities = XML_UTIL_ENTITIES_XML;
  37007. }
  37008. $tag = XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options['linebreak']);
  37009. } elseif (is_array($tag['content'])) {
  37010. $tag = $this->_serializeArray($tag['content'], $tag['qname'], $tag['attributes']);
  37011. } elseif (is_object($tag['content'])) {
  37012. $tag = $this->_serializeObject($tag['content'], $tag['qname'], $tag['attributes']);
  37013. } elseif (is_resource($tag['content'])) {
  37014. settype($tag['content'], 'string');
  37015. $tag = XML_Util::createTagFromArray($tag, $replaceEntities);
  37016. }
  37017. return $tag;
  37018. }
  37019. }
  37020. �������������������������������������������������������PEAR-1.10.16/PEAR/PackageFile/Parser/v1.php���������������������������������������������������������0000644�0001750�0001750�00000040207�14720722517�017446� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  37021. /**
  37022. * package.xml parsing class, package.xml version 1.0
  37023. *
  37024. * PHP versions 4 and 5
  37025. *
  37026. * @category pear
  37027. * @package PEAR
  37028. * @author Greg Beaver <cellog@php.net>
  37029. * @copyright 1997-2009 The Authors
  37030. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  37031. * @link http://pear.php.net/package/PEAR
  37032. * @since File available since Release 1.4.0a1
  37033. */
  37034. /**
  37035. * package.xml abstraction class
  37036. */
  37037. require_once 'PEAR/PackageFile/v1.php';
  37038. /**
  37039. * Parser for package.xml version 1.0
  37040. * @category pear
  37041. * @package PEAR
  37042. * @author Greg Beaver <cellog@php.net>
  37043. * @copyright 1997-2009 The Authors
  37044. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  37045. * @version Release: @PEAR-VER@
  37046. * @link http://pear.php.net/package/PEAR
  37047. * @since Class available since Release 1.4.0a1
  37048. */
  37049. class PEAR_PackageFile_Parser_v1
  37050. {
  37051. var $_registry;
  37052. var $_config;
  37053. var $_logger;
  37054. /**
  37055. * BC hack to allow PEAR_Common::infoFromString() to sort of
  37056. * work with the version 2.0 format - there's no filelist though
  37057. * @param PEAR_PackageFile_v2
  37058. */
  37059. function fromV2($packagefile)
  37060. {
  37061. $info = $packagefile->getArray(true);
  37062. $ret = new PEAR_PackageFile_v1;
  37063. $ret->fromArray($info['old']);
  37064. }
  37065. function setConfig(&$c)
  37066. {
  37067. $this->_config = &$c;
  37068. $this->_registry = &$c->getRegistry();
  37069. }
  37070. function setLogger(&$l)
  37071. {
  37072. $this->_logger = &$l;
  37073. }
  37074. /**
  37075. * @param string contents of package.xml file, version 1.0
  37076. * @return bool success of parsing
  37077. */
  37078. function &parse($data, $file, $archive = false)
  37079. {
  37080. if (!extension_loaded('xml')) {
  37081. return PEAR::raiseError('Cannot create xml parser for parsing package.xml, no xml extension');
  37082. }
  37083. $xp = xml_parser_create();
  37084. if (!$xp) {
  37085. $a = &PEAR::raiseError('Cannot create xml parser for parsing package.xml');
  37086. return $a;
  37087. }
  37088. xml_set_object($xp, $this);
  37089. xml_set_element_handler($xp, '_element_start_1_0', '_element_end_1_0');
  37090. xml_set_character_data_handler($xp, '_pkginfo_cdata_1_0');
  37091. xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false);
  37092. $this->element_stack = array();
  37093. $this->_packageInfo = array('provides' => array());
  37094. $this->current_element = false;
  37095. unset($this->dir_install);
  37096. $this->_packageInfo['filelist'] = array();
  37097. $this->filelist =& $this->_packageInfo['filelist'];
  37098. $this->dir_names = array();
  37099. $this->in_changelog = false;
  37100. $this->d_i = 0;
  37101. $this->cdata = '';
  37102. $this->_isValid = true;
  37103. if (!xml_parse($xp, $data, 1)) {
  37104. $code = xml_get_error_code($xp);
  37105. $line = xml_get_current_line_number($xp);
  37106. xml_parser_free($xp);
  37107. $a = PEAR::raiseError(sprintf("XML error: %s at line %d",
  37108. $str = xml_error_string($code), $line), 2);
  37109. return $a;
  37110. }
  37111. xml_parser_free($xp);
  37112. $pf = new PEAR_PackageFile_v1;
  37113. $pf->setConfig($this->_config);
  37114. if (isset($this->_logger)) {
  37115. $pf->setLogger($this->_logger);
  37116. }
  37117. $pf->setPackagefile($file, $archive);
  37118. $pf->fromArray($this->_packageInfo);
  37119. return $pf;
  37120. }
  37121. // {{{ _unIndent()
  37122. /**
  37123. * Unindent given string
  37124. *
  37125. * @param string $str The string that has to be unindented.
  37126. * @return string
  37127. * @access private
  37128. */
  37129. function _unIndent($str)
  37130. {
  37131. // remove leading newlines
  37132. $str = preg_replace('/^[\r\n]+/', '', $str);
  37133. // find whitespace at the beginning of the first line
  37134. $indent_len = strspn($str, " \t");
  37135. $indent = substr($str, 0, $indent_len);
  37136. $data = '';
  37137. // remove the same amount of whitespace from following lines
  37138. foreach (explode("\n", $str) as $line) {
  37139. if (substr($line, 0, $indent_len) == $indent) {
  37140. $data .= substr($line, $indent_len) . "\n";
  37141. } elseif (trim(substr($line, 0, $indent_len))) {
  37142. $data .= ltrim($line);
  37143. }
  37144. }
  37145. return $data;
  37146. }
  37147. // Support for package DTD v1.0:
  37148. // {{{ _element_start_1_0()
  37149. /**
  37150. * XML parser callback for ending elements. Used for version 1.0
  37151. * packages.
  37152. *
  37153. * @param resource $xp XML parser resource
  37154. * @param string $name name of ending element
  37155. *
  37156. * @return void
  37157. *
  37158. * @access private
  37159. */
  37160. function _element_start_1_0($xp, $name, $attribs)
  37161. {
  37162. array_push($this->element_stack, $name);
  37163. $this->current_element = $name;
  37164. $spos = sizeof($this->element_stack) - 2;
  37165. $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : '';
  37166. $this->current_attributes = $attribs;
  37167. $this->cdata = '';
  37168. switch ($name) {
  37169. case 'dir':
  37170. if ($this->in_changelog) {
  37171. break;
  37172. }
  37173. if (array_key_exists('name', $attribs) && $attribs['name'] != '/') {
  37174. $attribs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
  37175. $attribs['name']);
  37176. if (strrpos($attribs['name'], '/') === strlen($attribs['name']) - 1) {
  37177. $attribs['name'] = substr($attribs['name'], 0,
  37178. strlen($attribs['name']) - 1);
  37179. }
  37180. if (strpos($attribs['name'], '/') === 0) {
  37181. $attribs['name'] = substr($attribs['name'], 1);
  37182. }
  37183. $this->dir_names[] = $attribs['name'];
  37184. }
  37185. if (isset($attribs['baseinstalldir'])) {
  37186. $this->dir_install = $attribs['baseinstalldir'];
  37187. }
  37188. if (isset($attribs['role'])) {
  37189. $this->dir_role = $attribs['role'];
  37190. }
  37191. break;
  37192. case 'file':
  37193. if ($this->in_changelog) {
  37194. break;
  37195. }
  37196. if (isset($attribs['name'])) {
  37197. $path = '';
  37198. if (count($this->dir_names)) {
  37199. foreach ($this->dir_names as $dir) {
  37200. $path .= $dir . '/';
  37201. }
  37202. }
  37203. $path .= preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
  37204. $attribs['name']);
  37205. unset($attribs['name']);
  37206. $this->current_path = $path;
  37207. $this->filelist[$path] = $attribs;
  37208. // Set the baseinstalldir only if the file don't have this attrib
  37209. if (!isset($this->filelist[$path]['baseinstalldir']) &&
  37210. isset($this->dir_install))
  37211. {
  37212. $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
  37213. }
  37214. // Set the Role
  37215. if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
  37216. $this->filelist[$path]['role'] = $this->dir_role;
  37217. }
  37218. }
  37219. break;
  37220. case 'replace':
  37221. if (!$this->in_changelog) {
  37222. $this->filelist[$this->current_path]['replacements'][] = $attribs;
  37223. }
  37224. break;
  37225. case 'maintainers':
  37226. $this->_packageInfo['maintainers'] = array();
  37227. $this->m_i = 0; // maintainers array index
  37228. break;
  37229. case 'maintainer':
  37230. // compatibility check
  37231. if (!isset($this->_packageInfo['maintainers'])) {
  37232. $this->_packageInfo['maintainers'] = array();
  37233. $this->m_i = 0;
  37234. }
  37235. $this->_packageInfo['maintainers'][$this->m_i] = array();
  37236. $this->current_maintainer =& $this->_packageInfo['maintainers'][$this->m_i];
  37237. break;
  37238. case 'changelog':
  37239. $this->_packageInfo['changelog'] = array();
  37240. $this->c_i = 0; // changelog array index
  37241. $this->in_changelog = true;
  37242. break;
  37243. case 'release':
  37244. if ($this->in_changelog) {
  37245. $this->_packageInfo['changelog'][$this->c_i] = array();
  37246. $this->current_release = &$this->_packageInfo['changelog'][$this->c_i];
  37247. } else {
  37248. $this->current_release = &$this->_packageInfo;
  37249. }
  37250. break;
  37251. case 'deps':
  37252. if (!$this->in_changelog) {
  37253. $this->_packageInfo['release_deps'] = array();
  37254. }
  37255. break;
  37256. case 'dep':
  37257. // dependencies array index
  37258. if (!$this->in_changelog) {
  37259. $this->d_i++;
  37260. isset($attribs['type']) ? ($attribs['type'] = strtolower($attribs['type'])) : false;
  37261. $this->_packageInfo['release_deps'][$this->d_i] = $attribs;
  37262. }
  37263. break;
  37264. case 'configureoptions':
  37265. if (!$this->in_changelog) {
  37266. $this->_packageInfo['configure_options'] = array();
  37267. }
  37268. break;
  37269. case 'configureoption':
  37270. if (!$this->in_changelog) {
  37271. $this->_packageInfo['configure_options'][] = $attribs;
  37272. }
  37273. break;
  37274. case 'provides':
  37275. if (empty($attribs['type']) || empty($attribs['name'])) {
  37276. break;
  37277. }
  37278. $attribs['explicit'] = true;
  37279. $this->_packageInfo['provides']["$attribs[type];$attribs[name]"] = $attribs;
  37280. break;
  37281. case 'package' :
  37282. if (isset($attribs['version'])) {
  37283. $this->_packageInfo['xsdversion'] = trim($attribs['version']);
  37284. } else {
  37285. $this->_packageInfo['xsdversion'] = '1.0';
  37286. }
  37287. if (isset($attribs['packagerversion'])) {
  37288. $this->_packageInfo['packagerversion'] = $attribs['packagerversion'];
  37289. }
  37290. break;
  37291. }
  37292. }
  37293. // }}}
  37294. // {{{ _element_end_1_0()
  37295. /**
  37296. * XML parser callback for ending elements. Used for version 1.0
  37297. * packages.
  37298. *
  37299. * @param resource $xp XML parser resource
  37300. * @param string $name name of ending element
  37301. *
  37302. * @return void
  37303. *
  37304. * @access private
  37305. */
  37306. function _element_end_1_0($xp, $name)
  37307. {
  37308. $data = trim($this->cdata);
  37309. switch ($name) {
  37310. case 'name':
  37311. switch ($this->prev_element) {
  37312. case 'package':
  37313. $this->_packageInfo['package'] = $data;
  37314. break;
  37315. case 'maintainer':
  37316. $this->current_maintainer['name'] = $data;
  37317. break;
  37318. }
  37319. break;
  37320. case 'extends' :
  37321. $this->_packageInfo['extends'] = $data;
  37322. break;
  37323. case 'summary':
  37324. $this->_packageInfo['summary'] = $data;
  37325. break;
  37326. case 'description':
  37327. $data = $this->_unIndent($this->cdata);
  37328. $this->_packageInfo['description'] = $data;
  37329. break;
  37330. case 'user':
  37331. $this->current_maintainer['handle'] = $data;
  37332. break;
  37333. case 'email':
  37334. $this->current_maintainer['email'] = $data;
  37335. break;
  37336. case 'role':
  37337. $this->current_maintainer['role'] = $data;
  37338. break;
  37339. case 'version':
  37340. if ($this->in_changelog) {
  37341. $this->current_release['version'] = $data;
  37342. } else {
  37343. $this->_packageInfo['version'] = $data;
  37344. }
  37345. break;
  37346. case 'date':
  37347. if ($this->in_changelog) {
  37348. $this->current_release['release_date'] = $data;
  37349. } else {
  37350. $this->_packageInfo['release_date'] = $data;
  37351. }
  37352. break;
  37353. case 'notes':
  37354. // try to "de-indent" release notes in case someone
  37355. // has been over-indenting their xml ;-)
  37356. // Trim only on the right side
  37357. $data = rtrim($this->_unIndent($this->cdata));
  37358. if ($this->in_changelog) {
  37359. $this->current_release['release_notes'] = $data;
  37360. } else {
  37361. $this->_packageInfo['release_notes'] = $data;
  37362. }
  37363. break;
  37364. case 'warnings':
  37365. if ($this->in_changelog) {
  37366. $this->current_release['release_warnings'] = $data;
  37367. } else {
  37368. $this->_packageInfo['release_warnings'] = $data;
  37369. }
  37370. break;
  37371. case 'state':
  37372. if ($this->in_changelog) {
  37373. $this->current_release['release_state'] = $data;
  37374. } else {
  37375. $this->_packageInfo['release_state'] = $data;
  37376. }
  37377. break;
  37378. case 'license':
  37379. if ($this->in_changelog) {
  37380. $this->current_release['release_license'] = $data;
  37381. } else {
  37382. $this->_packageInfo['release_license'] = $data;
  37383. }
  37384. break;
  37385. case 'dep':
  37386. if ($data && !$this->in_changelog) {
  37387. $this->_packageInfo['release_deps'][$this->d_i]['name'] = $data;
  37388. }
  37389. break;
  37390. case 'dir':
  37391. if ($this->in_changelog) {
  37392. break;
  37393. }
  37394. array_pop($this->dir_names);
  37395. break;
  37396. case 'file':
  37397. if ($this->in_changelog) {
  37398. break;
  37399. }
  37400. if ($data) {
  37401. $path = '';
  37402. if (count($this->dir_names)) {
  37403. foreach ($this->dir_names as $dir) {
  37404. $path .= $dir . '/';
  37405. }
  37406. }
  37407. $path .= $data;
  37408. $this->filelist[$path] = $this->current_attributes;
  37409. // Set the baseinstalldir only if the file don't have this attrib
  37410. if (!isset($this->filelist[$path]['baseinstalldir']) &&
  37411. isset($this->dir_install))
  37412. {
  37413. $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
  37414. }
  37415. // Set the Role
  37416. if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
  37417. $this->filelist[$path]['role'] = $this->dir_role;
  37418. }
  37419. }
  37420. break;
  37421. case 'maintainer':
  37422. if (empty($this->_packageInfo['maintainers'][$this->m_i]['role'])) {
  37423. $this->_packageInfo['maintainers'][$this->m_i]['role'] = 'lead';
  37424. }
  37425. $this->m_i++;
  37426. break;
  37427. case 'release':
  37428. if ($this->in_changelog) {
  37429. $this->c_i++;
  37430. }
  37431. break;
  37432. case 'changelog':
  37433. $this->in_changelog = false;
  37434. break;
  37435. }
  37436. array_pop($this->element_stack);
  37437. $spos = sizeof($this->element_stack) - 1;
  37438. $this->current_element = ($spos > 0) ? $this->element_stack[$spos] : '';
  37439. $this->cdata = '';
  37440. }
  37441. // }}}
  37442. // {{{ _pkginfo_cdata_1_0()
  37443. /**
  37444. * XML parser callback for character data. Used for version 1.0
  37445. * packages.
  37446. *
  37447. * @param resource $xp XML parser resource
  37448. * @param string $name character data
  37449. *
  37450. * @return void
  37451. *
  37452. * @access private
  37453. */
  37454. function _pkginfo_cdata_1_0($xp, $data)
  37455. {
  37456. if (isset($this->cdata)) {
  37457. $this->cdata .= $data;
  37458. }
  37459. }
  37460. // }}}
  37461. }
  37462. ?>�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/PackageFile/Parser/v2.php���������������������������������������������������������0000644�0001750�0001750�00000006110�14720722517�017442� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  37463. /**
  37464. * package.xml parsing class, package.xml version 2.0
  37465. *
  37466. * PHP versions 4 and 5
  37467. *
  37468. * @category pear
  37469. * @package PEAR
  37470. * @author Greg Beaver <cellog@php.net>
  37471. * @copyright 1997-2009 The Authors
  37472. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  37473. * @link http://pear.php.net/package/PEAR
  37474. * @since File available since Release 1.4.0a1
  37475. */
  37476. /**
  37477. * base xml parser class
  37478. */
  37479. require_once 'PEAR/XMLParser.php';
  37480. require_once 'PEAR/PackageFile/v2.php';
  37481. /**
  37482. * Parser for package.xml version 2.0
  37483. * @category pear
  37484. * @package PEAR
  37485. * @author Greg Beaver <cellog@php.net>
  37486. * @copyright 1997-2009 The Authors
  37487. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  37488. * @version Release: @PEAR-VER@
  37489. * @link http://pear.php.net/package/PEAR
  37490. * @since Class available since Release 1.4.0a1
  37491. */
  37492. class PEAR_PackageFile_Parser_v2 extends PEAR_XMLParser
  37493. {
  37494. var $_config;
  37495. var $_logger;
  37496. var $_registry;
  37497. function setConfig(&$c)
  37498. {
  37499. $this->_config = &$c;
  37500. $this->_registry = &$c->getRegistry();
  37501. }
  37502. function setLogger(&$l)
  37503. {
  37504. $this->_logger = &$l;
  37505. }
  37506. /**
  37507. * Unindent given string
  37508. *
  37509. * @param string $str The string that has to be unindented.
  37510. * @return string
  37511. * @access private
  37512. */
  37513. function _unIndent($str)
  37514. {
  37515. // remove leading newlines
  37516. $str = preg_replace('/^[\r\n]+/', '', $str);
  37517. // find whitespace at the beginning of the first line
  37518. $indent_len = strspn($str, " \t");
  37519. $indent = substr($str, 0, $indent_len);
  37520. $data = '';
  37521. // remove the same amount of whitespace from following lines
  37522. foreach (explode("\n", $str) as $line) {
  37523. if (substr($line, 0, $indent_len) == $indent) {
  37524. $data .= substr($line, $indent_len) . "\n";
  37525. } else {
  37526. $data .= $line . "\n";
  37527. }
  37528. }
  37529. return $data;
  37530. }
  37531. /**
  37532. * post-process data
  37533. *
  37534. * @param string $data
  37535. * @param string $element element name
  37536. */
  37537. function postProcess($data, $element)
  37538. {
  37539. if ($element == 'notes') {
  37540. return trim($this->_unIndent($data));
  37541. }
  37542. return trim($data);
  37543. }
  37544. /**
  37545. * @param string
  37546. * @param string file name of the package.xml
  37547. * @param string|false name of the archive this package.xml came from, if any
  37548. * @param string class name to instantiate and return. This must be PEAR_PackageFile_v2 or
  37549. * a subclass
  37550. * @return PEAR_PackageFile_v2
  37551. */
  37552. function parse($data, $file = null, $archive = false, $class = 'PEAR_PackageFile_v2')
  37553. {
  37554. if (PEAR::isError($err = parent::parse($data))) {
  37555. return $err;
  37556. }
  37557. $ret = new $class;
  37558. $ret->encoding = $this->encoding;
  37559. $ret->setConfig($this->_config);
  37560. if (isset($this->_logger)) {
  37561. $ret->setLogger($this->_logger);
  37562. }
  37563. $ret->fromArray($this->_unserializedData);
  37564. $ret->setPackagefile($file, $archive);
  37565. return $ret;
  37566. }
  37567. }��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/PackageFile/v2/rw.php�������������������������������������������������������������0000644�0001750�0001750�00000173137�14720722517�016654� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  37568. /**
  37569. * PEAR_PackageFile_v2, package.xml version 2.0, read/write version
  37570. *
  37571. * PHP versions 4 and 5
  37572. *
  37573. * @category pear
  37574. * @package PEAR
  37575. * @author Greg Beaver <cellog@php.net>
  37576. * @copyright 1997-2009 The Authors
  37577. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  37578. * @link http://pear.php.net/package/PEAR
  37579. * @since File available since Release 1.4.0a8
  37580. */
  37581. /**
  37582. * For base class
  37583. */
  37584. require_once 'PEAR/PackageFile/v2.php';
  37585. /**
  37586. * @category pear
  37587. * @package PEAR
  37588. * @author Greg Beaver <cellog@php.net>
  37589. * @copyright 1997-2009 The Authors
  37590. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  37591. * @version Release: 1.10.16
  37592. * @link http://pear.php.net/package/PEAR
  37593. * @since Class available since Release 1.4.0a8
  37594. */
  37595. class PEAR_PackageFile_v2_rw extends PEAR_PackageFile_v2
  37596. {
  37597. /**
  37598. * @param string Extension name
  37599. * @return bool success of operation
  37600. */
  37601. function setProvidesExtension($extension)
  37602. {
  37603. if (in_array($this->getPackageType(),
  37604. array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
  37605. if (!isset($this->_packageInfo['providesextension'])) {
  37606. // ensure that the channel tag is set up in the right location
  37607. $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
  37608. array('usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease',
  37609. 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
  37610. 'bundle', 'changelog'),
  37611. $extension, 'providesextension');
  37612. }
  37613. $this->_packageInfo['providesextension'] = $extension;
  37614. return true;
  37615. }
  37616. return false;
  37617. }
  37618. function setPackage($package)
  37619. {
  37620. $this->_isValid = 0;
  37621. if (!isset($this->_packageInfo['attribs'])) {
  37622. $this->_packageInfo = array_merge(array('attribs' => array(
  37623. 'version' => '2.0',
  37624. 'xmlns' => 'http://pear.php.net/dtd/package-2.0',
  37625. 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
  37626. 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
  37627. 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
  37628. http://pear.php.net/dtd/tasks-1.0.xsd
  37629. http://pear.php.net/dtd/package-2.0
  37630. http://pear.php.net/dtd/package-2.0.xsd',
  37631. )), $this->_packageInfo);
  37632. }
  37633. if (!isset($this->_packageInfo['name'])) {
  37634. return $this->_packageInfo = array_merge(array('name' => $package),
  37635. $this->_packageInfo);
  37636. }
  37637. $this->_packageInfo['name'] = $package;
  37638. }
  37639. /**
  37640. * set this as a package.xml version 2.1
  37641. * @access private
  37642. */
  37643. function _setPackageVersion2_1()
  37644. {
  37645. $info = array(
  37646. 'version' => '2.1',
  37647. 'xmlns' => 'http://pear.php.net/dtd/package-2.1',
  37648. 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
  37649. 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
  37650. 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
  37651. http://pear.php.net/dtd/tasks-1.0.xsd
  37652. http://pear.php.net/dtd/package-2.1
  37653. http://pear.php.net/dtd/package-2.1.xsd',
  37654. );
  37655. if (!isset($this->_packageInfo['attribs'])) {
  37656. $this->_packageInfo = array_merge(array('attribs' => $info), $this->_packageInfo);
  37657. } else {
  37658. $this->_packageInfo['attribs'] = $info;
  37659. }
  37660. }
  37661. function setUri($uri)
  37662. {
  37663. unset($this->_packageInfo['channel']);
  37664. $this->_isValid = 0;
  37665. if (!isset($this->_packageInfo['uri'])) {
  37666. // ensure that the uri tag is set up in the right location
  37667. $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
  37668. array('extends', 'summary', 'description', 'lead',
  37669. 'developer', 'contributor', 'helper', 'date', 'time', 'version',
  37670. 'stability', 'license', 'notes', 'contents', 'compatible',
  37671. 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
  37672. 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
  37673. 'extbinrelease', 'bundle', 'changelog'), $uri, 'uri');
  37674. }
  37675. $this->_packageInfo['uri'] = $uri;
  37676. }
  37677. function setChannel($channel)
  37678. {
  37679. unset($this->_packageInfo['uri']);
  37680. $this->_isValid = 0;
  37681. if (!isset($this->_packageInfo['channel'])) {
  37682. // ensure that the channel tag is set up in the right location
  37683. $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
  37684. array('extends', 'summary', 'description', 'lead',
  37685. 'developer', 'contributor', 'helper', 'date', 'time', 'version',
  37686. 'stability', 'license', 'notes', 'contents', 'compatible',
  37687. 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
  37688. 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
  37689. 'extbinrelease', 'bundle', 'changelog'), $channel, 'channel');
  37690. }
  37691. $this->_packageInfo['channel'] = $channel;
  37692. }
  37693. function setExtends($extends)
  37694. {
  37695. $this->_isValid = 0;
  37696. if (!isset($this->_packageInfo['extends'])) {
  37697. // ensure that the extends tag is set up in the right location
  37698. $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
  37699. array('summary', 'description', 'lead',
  37700. 'developer', 'contributor', 'helper', 'date', 'time', 'version',
  37701. 'stability', 'license', 'notes', 'contents', 'compatible',
  37702. 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
  37703. 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
  37704. 'extbinrelease', 'bundle', 'changelog'), $extends, 'extends');
  37705. }
  37706. $this->_packageInfo['extends'] = $extends;
  37707. }
  37708. function setSummary($summary)
  37709. {
  37710. $this->_isValid = 0;
  37711. if (!isset($this->_packageInfo['summary'])) {
  37712. // ensure that the summary tag is set up in the right location
  37713. $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
  37714. array('description', 'lead',
  37715. 'developer', 'contributor', 'helper', 'date', 'time', 'version',
  37716. 'stability', 'license', 'notes', 'contents', 'compatible',
  37717. 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
  37718. 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
  37719. 'extbinrelease', 'bundle', 'changelog'), $summary, 'summary');
  37720. }
  37721. $this->_packageInfo['summary'] = $summary;
  37722. }
  37723. function setDescription($desc)
  37724. {
  37725. $this->_isValid = 0;
  37726. if (!isset($this->_packageInfo['description'])) {
  37727. // ensure that the description tag is set up in the right location
  37728. $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
  37729. array('lead',
  37730. 'developer', 'contributor', 'helper', 'date', 'time', 'version',
  37731. 'stability', 'license', 'notes', 'contents', 'compatible',
  37732. 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
  37733. 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
  37734. 'extbinrelease', 'bundle', 'changelog'), $desc, 'description');
  37735. }
  37736. $this->_packageInfo['description'] = $desc;
  37737. }
  37738. /**
  37739. * Adds a new maintainer - no checking of duplicates is performed, use
  37740. * updatemaintainer for that purpose.
  37741. */
  37742. function addMaintainer($role, $handle, $name, $email, $active = 'yes')
  37743. {
  37744. if (!in_array($role, array('lead', 'developer', 'contributor', 'helper'))) {
  37745. return false;
  37746. }
  37747. if (isset($this->_packageInfo[$role])) {
  37748. if (!isset($this->_packageInfo[$role][0])) {
  37749. $this->_packageInfo[$role] = array($this->_packageInfo[$role]);
  37750. }
  37751. $this->_packageInfo[$role][] =
  37752. array(
  37753. 'name' => $name,
  37754. 'user' => $handle,
  37755. 'email' => $email,
  37756. 'active' => $active,
  37757. );
  37758. } else {
  37759. $testarr = array('lead',
  37760. 'developer', 'contributor', 'helper', 'date', 'time', 'version',
  37761. 'stability', 'license', 'notes', 'contents', 'compatible',
  37762. 'dependencies', 'providesextension', 'usesrole', 'usestask',
  37763. 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease',
  37764. 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog');
  37765. foreach (array('lead', 'developer', 'contributor', 'helper') as $testrole) {
  37766. array_shift($testarr);
  37767. if ($role == $testrole) {
  37768. break;
  37769. }
  37770. }
  37771. if (!isset($this->_packageInfo[$role])) {
  37772. // ensure that the extends tag is set up in the right location
  37773. $this->_packageInfo = $this->_insertBefore($this->_packageInfo, $testarr,
  37774. array(), $role);
  37775. }
  37776. $this->_packageInfo[$role] =
  37777. array(
  37778. 'name' => $name,
  37779. 'user' => $handle,
  37780. 'email' => $email,
  37781. 'active' => $active,
  37782. );
  37783. }
  37784. $this->_isValid = 0;
  37785. }
  37786. function updateMaintainer($newrole, $handle, $name, $email, $active = 'yes')
  37787. {
  37788. $found = false;
  37789. foreach (array('lead', 'developer', 'contributor', 'helper') as $role) {
  37790. if (!isset($this->_packageInfo[$role])) {
  37791. continue;
  37792. }
  37793. $info = $this->_packageInfo[$role];
  37794. if (!isset($info[0])) {
  37795. if ($info['user'] == $handle) {
  37796. $found = true;
  37797. break;
  37798. }
  37799. }
  37800. foreach ($info as $i => $maintainer) {
  37801. if (is_array($maintainer) && $maintainer['user'] == $handle) {
  37802. $found = $i;
  37803. break 2;
  37804. }
  37805. }
  37806. }
  37807. if ($found === false) {
  37808. return $this->addMaintainer($newrole, $handle, $name, $email, $active);
  37809. }
  37810. if ($found !== false) {
  37811. if ($found === true) {
  37812. unset($this->_packageInfo[$role]);
  37813. } else {
  37814. unset($this->_packageInfo[$role][$found]);
  37815. $this->_packageInfo[$role] = array_values($this->_packageInfo[$role]);
  37816. }
  37817. }
  37818. $this->addMaintainer($newrole, $handle, $name, $email, $active);
  37819. $this->_isValid = 0;
  37820. }
  37821. function deleteMaintainer($handle)
  37822. {
  37823. $found = false;
  37824. foreach (array('lead', 'developer', 'contributor', 'helper') as $role) {
  37825. if (!isset($this->_packageInfo[$role])) {
  37826. continue;
  37827. }
  37828. if (!isset($this->_packageInfo[$role][0])) {
  37829. $this->_packageInfo[$role] = array($this->_packageInfo[$role]);
  37830. }
  37831. foreach ($this->_packageInfo[$role] as $i => $maintainer) {
  37832. if ($maintainer['user'] == $handle) {
  37833. $found = $i;
  37834. break;
  37835. }
  37836. }
  37837. if ($found !== false) {
  37838. unset($this->_packageInfo[$role][$found]);
  37839. if (!count($this->_packageInfo[$role]) && $role == 'lead') {
  37840. $this->_isValid = 0;
  37841. }
  37842. if (!count($this->_packageInfo[$role])) {
  37843. unset($this->_packageInfo[$role]);
  37844. return true;
  37845. }
  37846. $this->_packageInfo[$role] =
  37847. array_values($this->_packageInfo[$role]);
  37848. if (count($this->_packageInfo[$role]) == 1) {
  37849. $this->_packageInfo[$role] = $this->_packageInfo[$role][0];
  37850. }
  37851. return true;
  37852. }
  37853. if (count($this->_packageInfo[$role]) == 1) {
  37854. $this->_packageInfo[$role] = $this->_packageInfo[$role][0];
  37855. }
  37856. }
  37857. return false;
  37858. }
  37859. function setReleaseVersion($version)
  37860. {
  37861. if (isset($this->_packageInfo['version']) &&
  37862. isset($this->_packageInfo['version']['release'])) {
  37863. unset($this->_packageInfo['version']['release']);
  37864. }
  37865. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $version, array(
  37866. 'version' => array('stability', 'license', 'notes', 'contents', 'compatible',
  37867. 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
  37868. 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
  37869. 'extbinrelease', 'bundle', 'changelog'),
  37870. 'release' => array('api')));
  37871. $this->_isValid = 0;
  37872. }
  37873. function setAPIVersion($version)
  37874. {
  37875. if (isset($this->_packageInfo['version']) &&
  37876. isset($this->_packageInfo['version']['api'])) {
  37877. unset($this->_packageInfo['version']['api']);
  37878. }
  37879. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $version, array(
  37880. 'version' => array('stability', 'license', 'notes', 'contents', 'compatible',
  37881. 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
  37882. 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
  37883. 'extbinrelease', 'bundle', 'changelog'),
  37884. 'api' => array()));
  37885. $this->_isValid = 0;
  37886. }
  37887. /**
  37888. * snapshot|devel|alpha|beta|stable
  37889. */
  37890. function setReleaseStability($state)
  37891. {
  37892. if (isset($this->_packageInfo['stability']) &&
  37893. isset($this->_packageInfo['stability']['release'])) {
  37894. unset($this->_packageInfo['stability']['release']);
  37895. }
  37896. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $state, array(
  37897. 'stability' => array('license', 'notes', 'contents', 'compatible',
  37898. 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
  37899. 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
  37900. 'extbinrelease', 'bundle', 'changelog'),
  37901. 'release' => array('api')));
  37902. $this->_isValid = 0;
  37903. }
  37904. /**
  37905. * @param devel|alpha|beta|stable
  37906. */
  37907. function setAPIStability($state)
  37908. {
  37909. if (isset($this->_packageInfo['stability']) &&
  37910. isset($this->_packageInfo['stability']['api'])) {
  37911. unset($this->_packageInfo['stability']['api']);
  37912. }
  37913. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $state, array(
  37914. 'stability' => array('license', 'notes', 'contents', 'compatible',
  37915. 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
  37916. 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
  37917. 'extbinrelease', 'bundle', 'changelog'),
  37918. 'api' => array()));
  37919. $this->_isValid = 0;
  37920. }
  37921. function setLicense($license, $uri = false, $filesource = false)
  37922. {
  37923. if (!isset($this->_packageInfo['license'])) {
  37924. // ensure that the license tag is set up in the right location
  37925. $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
  37926. array('notes', 'contents', 'compatible',
  37927. 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
  37928. 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
  37929. 'extbinrelease', 'bundle', 'changelog'), 0, 'license');
  37930. }
  37931. if ($uri || $filesource) {
  37932. $attribs = array();
  37933. if ($uri) {
  37934. $attribs['uri'] = $uri;
  37935. }
  37936. $uri = true; // for test below
  37937. if ($filesource) {
  37938. $attribs['filesource'] = $filesource;
  37939. }
  37940. }
  37941. $license = $uri ? array('attribs' => $attribs, '_content' => $license) : $license;
  37942. $this->_packageInfo['license'] = $license;
  37943. $this->_isValid = 0;
  37944. }
  37945. function setNotes($notes)
  37946. {
  37947. $this->_isValid = 0;
  37948. if (!isset($this->_packageInfo['notes'])) {
  37949. // ensure that the notes tag is set up in the right location
  37950. $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
  37951. array('contents', 'compatible',
  37952. 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
  37953. 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
  37954. 'extbinrelease', 'bundle', 'changelog'), $notes, 'notes');
  37955. }
  37956. $this->_packageInfo['notes'] = $notes;
  37957. }
  37958. /**
  37959. * This is only used at install-time, after all serialization
  37960. * is over.
  37961. * @param string file name
  37962. * @param string installed path
  37963. */
  37964. function setInstalledAs($file, $path)
  37965. {
  37966. if ($path) {
  37967. return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
  37968. }
  37969. unset($this->_packageInfo['filelist'][$file]['installed_as']);
  37970. }
  37971. /**
  37972. * This is only used at install-time, after all serialization
  37973. * is over.
  37974. */
  37975. function installedFile($file, $atts)
  37976. {
  37977. if (isset($this->_packageInfo['filelist'][$file])) {
  37978. $this->_packageInfo['filelist'][$file] =
  37979. array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']);
  37980. } else {
  37981. $this->_packageInfo['filelist'][$file] = $atts['attribs'];
  37982. }
  37983. }
  37984. /**
  37985. * Reset the listing of package contents
  37986. * @param string base installation dir for the whole package, if any
  37987. */
  37988. function clearContents($baseinstall = false)
  37989. {
  37990. $this->_filesValid = false;
  37991. $this->_isValid = 0;
  37992. if (!isset($this->_packageInfo['contents'])) {
  37993. $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
  37994. array('compatible',
  37995. 'dependencies', 'providesextension', 'usesrole', 'usestask',
  37996. 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease',
  37997. 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
  37998. 'bundle', 'changelog'), array(), 'contents');
  37999. }
  38000. if ($this->getPackageType() != 'bundle') {
  38001. $this->_packageInfo['contents'] =
  38002. array('dir' => array('attribs' => array('name' => '/')));
  38003. if ($baseinstall) {
  38004. $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'] = $baseinstall;
  38005. }
  38006. } else {
  38007. $this->_packageInfo['contents'] = array('bundledpackage' => array());
  38008. }
  38009. }
  38010. /**
  38011. * @param string relative path of the bundled package.
  38012. */
  38013. function addBundledPackage($path)
  38014. {
  38015. if ($this->getPackageType() != 'bundle') {
  38016. return false;
  38017. }
  38018. $this->_filesValid = false;
  38019. $this->_isValid = 0;
  38020. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $path, array(
  38021. 'contents' => array('compatible', 'dependencies', 'providesextension',
  38022. 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease',
  38023. 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
  38024. 'bundle', 'changelog'),
  38025. 'bundledpackage' => array()));
  38026. }
  38027. /**
  38028. * @param string file name
  38029. * @param PEAR_Task_Common a read/write task
  38030. */
  38031. function addTaskToFile($filename, $task)
  38032. {
  38033. if (!method_exists($task, 'getXml')) {
  38034. return false;
  38035. }
  38036. if (!method_exists($task, 'getName')) {
  38037. return false;
  38038. }
  38039. if (!method_exists($task, 'validate')) {
  38040. return false;
  38041. }
  38042. if (!$task->validate()) {
  38043. return false;
  38044. }
  38045. if (!isset($this->_packageInfo['contents']['dir']['file'])) {
  38046. return false;
  38047. }
  38048. $this->getTasksNs(); // discover the tasks namespace if not done already
  38049. $files = $this->_packageInfo['contents']['dir']['file'];
  38050. if (!isset($files[0])) {
  38051. $files = array($files);
  38052. $ind = false;
  38053. } else {
  38054. $ind = true;
  38055. }
  38056. foreach ($files as $i => $file) {
  38057. if (isset($file['attribs'])) {
  38058. if ($file['attribs']['name'] == $filename) {
  38059. if ($ind) {
  38060. $t = isset($this->_packageInfo['contents']['dir']['file'][$i]
  38061. ['attribs'][$this->_tasksNs .
  38062. ':' . $task->getName()]) ?
  38063. $this->_packageInfo['contents']['dir']['file'][$i]
  38064. ['attribs'][$this->_tasksNs .
  38065. ':' . $task->getName()] : false;
  38066. if ($t && !isset($t[0])) {
  38067. $this->_packageInfo['contents']['dir']['file'][$i]
  38068. [$this->_tasksNs . ':' . $task->getName()] = array($t);
  38069. }
  38070. $this->_packageInfo['contents']['dir']['file'][$i][$this->_tasksNs .
  38071. ':' . $task->getName()][] = $task->getXml();
  38072. } else {
  38073. $t = isset($this->_packageInfo['contents']['dir']['file']
  38074. ['attribs'][$this->_tasksNs .
  38075. ':' . $task->getName()]) ? $this->_packageInfo['contents']['dir']['file']
  38076. ['attribs'][$this->_tasksNs .
  38077. ':' . $task->getName()] : false;
  38078. if ($t && !isset($t[0])) {
  38079. $this->_packageInfo['contents']['dir']['file']
  38080. [$this->_tasksNs . ':' . $task->getName()] = array($t);
  38081. }
  38082. $this->_packageInfo['contents']['dir']['file'][$this->_tasksNs .
  38083. ':' . $task->getName()][] = $task->getXml();
  38084. }
  38085. return true;
  38086. }
  38087. }
  38088. }
  38089. return false;
  38090. }
  38091. /**
  38092. * @param string path to the file
  38093. * @param string filename
  38094. * @param array extra attributes
  38095. */
  38096. function addFile($dir, $file, $attrs)
  38097. {
  38098. if ($this->getPackageType() == 'bundle') {
  38099. return false;
  38100. }
  38101. $this->_filesValid = false;
  38102. $this->_isValid = 0;
  38103. $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir);
  38104. if ($dir == '/' || $dir == '') {
  38105. $dir = '';
  38106. } else {
  38107. $dir .= '/';
  38108. }
  38109. $attrs['name'] = $dir . $file;
  38110. if (!isset($this->_packageInfo['contents'])) {
  38111. // ensure that the contents tag is set up
  38112. $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
  38113. array('compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask',
  38114. 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease',
  38115. 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
  38116. 'bundle', 'changelog'), array(), 'contents');
  38117. }
  38118. if (isset($this->_packageInfo['contents']['dir']['file'])) {
  38119. if (!isset($this->_packageInfo['contents']['dir']['file'][0])) {
  38120. $this->_packageInfo['contents']['dir']['file'] =
  38121. array($this->_packageInfo['contents']['dir']['file']);
  38122. }
  38123. $this->_packageInfo['contents']['dir']['file'][]['attribs'] = $attrs;
  38124. } else {
  38125. $this->_packageInfo['contents']['dir']['file']['attribs'] = $attrs;
  38126. }
  38127. }
  38128. /**
  38129. * @param string Dependent package name
  38130. * @param string Dependent package's channel name
  38131. * @param string minimum version of specified package that this release is guaranteed to be
  38132. * compatible with
  38133. * @param string maximum version of specified package that this release is guaranteed to be
  38134. * compatible with
  38135. * @param string versions of specified package that this release is not compatible with
  38136. */
  38137. function addCompatiblePackage($name, $channel, $min, $max, $exclude = false)
  38138. {
  38139. $this->_isValid = 0;
  38140. $set = array(
  38141. 'name' => $name,
  38142. 'channel' => $channel,
  38143. 'min' => $min,
  38144. 'max' => $max,
  38145. );
  38146. if ($exclude) {
  38147. $set['exclude'] = $exclude;
  38148. }
  38149. $this->_isValid = 0;
  38150. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array(
  38151. 'compatible' => array('dependencies', 'providesextension', 'usesrole', 'usestask',
  38152. 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
  38153. 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')
  38154. ));
  38155. }
  38156. /**
  38157. * Removes the <usesrole> tag entirely
  38158. */
  38159. function resetUsesrole()
  38160. {
  38161. if (isset($this->_packageInfo['usesrole'])) {
  38162. unset($this->_packageInfo['usesrole']);
  38163. }
  38164. }
  38165. /**
  38166. * @param string
  38167. * @param string package name or uri
  38168. * @param string channel name if non-uri
  38169. */
  38170. function addUsesrole($role, $packageOrUri, $channel = false) {
  38171. $set = array('role' => $role);
  38172. if ($channel) {
  38173. $set['package'] = $packageOrUri;
  38174. $set['channel'] = $channel;
  38175. } else {
  38176. $set['uri'] = $packageOrUri;
  38177. }
  38178. $this->_isValid = 0;
  38179. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array(
  38180. 'usesrole' => array('usestask', 'srcpackage', 'srcuri',
  38181. 'phprelease', 'extsrcrelease', 'extbinrelease',
  38182. 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')
  38183. ));
  38184. }
  38185. /**
  38186. * Removes the <usestask> tag entirely
  38187. */
  38188. function resetUsestask()
  38189. {
  38190. if (isset($this->_packageInfo['usestask'])) {
  38191. unset($this->_packageInfo['usestask']);
  38192. }
  38193. }
  38194. /**
  38195. * @param string
  38196. * @param string package name or uri
  38197. * @param string channel name if non-uri
  38198. */
  38199. function addUsestask($task, $packageOrUri, $channel = false) {
  38200. $set = array('task' => $task);
  38201. if ($channel) {
  38202. $set['package'] = $packageOrUri;
  38203. $set['channel'] = $channel;
  38204. } else {
  38205. $set['uri'] = $packageOrUri;
  38206. }
  38207. $this->_isValid = 0;
  38208. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array(
  38209. 'usestask' => array('srcpackage', 'srcuri',
  38210. 'phprelease', 'extsrcrelease', 'extbinrelease',
  38211. 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')
  38212. ));
  38213. }
  38214. /**
  38215. * Remove all compatible tags
  38216. */
  38217. function clearCompatible()
  38218. {
  38219. unset($this->_packageInfo['compatible']);
  38220. }
  38221. /**
  38222. * Reset dependencies prior to adding new ones
  38223. */
  38224. function clearDeps()
  38225. {
  38226. if (!isset($this->_packageInfo['dependencies'])) {
  38227. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array(),
  38228. array(
  38229. 'dependencies' => array('providesextension', 'usesrole', 'usestask',
  38230. 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
  38231. 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')));
  38232. }
  38233. $this->_packageInfo['dependencies'] = array();
  38234. }
  38235. /**
  38236. * @param string minimum PHP version allowed
  38237. * @param string maximum PHP version allowed
  38238. * @param array $exclude incompatible PHP versions
  38239. */
  38240. function setPhpDep($min, $max = false, $exclude = false)
  38241. {
  38242. $this->_isValid = 0;
  38243. $dep =
  38244. array(
  38245. 'min' => $min,
  38246. );
  38247. if ($max) {
  38248. $dep['max'] = $max;
  38249. }
  38250. if ($exclude) {
  38251. if (count($exclude) == 1) {
  38252. $exclude = $exclude[0];
  38253. }
  38254. $dep['exclude'] = $exclude;
  38255. }
  38256. if (isset($this->_packageInfo['dependencies']['required']['php'])) {
  38257. $this->_stack->push(__FUNCTION__, 'warning', array('dep' =>
  38258. $this->_packageInfo['dependencies']['required']['php']),
  38259. 'warning: PHP dependency already exists, overwriting');
  38260. unset($this->_packageInfo['dependencies']['required']['php']);
  38261. }
  38262. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
  38263. array(
  38264. 'dependencies' => array('providesextension', 'usesrole', 'usestask',
  38265. 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
  38266. 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
  38267. 'required' => array('optional', 'group'),
  38268. 'php' => array('pearinstaller', 'package', 'subpackage', 'extension', 'os', 'arch')
  38269. ));
  38270. return true;
  38271. }
  38272. /**
  38273. * @param string minimum allowed PEAR installer version
  38274. * @param string maximum allowed PEAR installer version
  38275. * @param string recommended PEAR installer version
  38276. * @param array incompatible version of the PEAR installer
  38277. */
  38278. function setPearinstallerDep($min, $max = false, $recommended = false, $exclude = false)
  38279. {
  38280. $this->_isValid = 0;
  38281. $dep =
  38282. array(
  38283. 'min' => $min,
  38284. );
  38285. if ($max) {
  38286. $dep['max'] = $max;
  38287. }
  38288. if ($recommended) {
  38289. $dep['recommended'] = $recommended;
  38290. }
  38291. if ($exclude) {
  38292. if (count($exclude) == 1) {
  38293. $exclude = $exclude[0];
  38294. }
  38295. $dep['exclude'] = $exclude;
  38296. }
  38297. if (isset($this->_packageInfo['dependencies']['required']['pearinstaller'])) {
  38298. $this->_stack->push(__FUNCTION__, 'warning', array('dep' =>
  38299. $this->_packageInfo['dependencies']['required']['pearinstaller']),
  38300. 'warning: PEAR Installer dependency already exists, overwriting');
  38301. unset($this->_packageInfo['dependencies']['required']['pearinstaller']);
  38302. }
  38303. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
  38304. array(
  38305. 'dependencies' => array('providesextension', 'usesrole', 'usestask',
  38306. 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
  38307. 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
  38308. 'required' => array('optional', 'group'),
  38309. 'pearinstaller' => array('package', 'subpackage', 'extension', 'os', 'arch')
  38310. ));
  38311. }
  38312. /**
  38313. * Mark a package as conflicting with this package
  38314. * @param string package name
  38315. * @param string package channel
  38316. * @param string extension this package provides, if any
  38317. * @param string|false minimum version required
  38318. * @param string|false maximum version allowed
  38319. * @param array|false versions to exclude from installation
  38320. */
  38321. function addConflictingPackageDepWithChannel($name, $channel,
  38322. $providesextension = false, $min = false, $max = false, $exclude = false)
  38323. {
  38324. $this->_isValid = 0;
  38325. $dep = $this->_constructDep($name, $channel, false, $min, $max, false,
  38326. $exclude, $providesextension, false, true);
  38327. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
  38328. array(
  38329. 'dependencies' => array('providesextension', 'usesrole', 'usestask',
  38330. 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
  38331. 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
  38332. 'required' => array('optional', 'group'),
  38333. 'package' => array('subpackage', 'extension', 'os', 'arch')
  38334. ));
  38335. }
  38336. /**
  38337. * Mark a package as conflicting with this package
  38338. * @param string package name
  38339. * @param string package channel
  38340. * @param string extension this package provides, if any
  38341. */
  38342. function addConflictingPackageDepWithUri($name, $uri, $providesextension = false)
  38343. {
  38344. $this->_isValid = 0;
  38345. $dep =
  38346. array(
  38347. 'name' => $name,
  38348. 'uri' => $uri,
  38349. 'conflicts' => '',
  38350. );
  38351. if ($providesextension) {
  38352. $dep['providesextension'] = $providesextension;
  38353. }
  38354. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
  38355. array(
  38356. 'dependencies' => array('providesextension', 'usesrole', 'usestask',
  38357. 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
  38358. 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
  38359. 'required' => array('optional', 'group'),
  38360. 'package' => array('subpackage', 'extension', 'os', 'arch')
  38361. ));
  38362. }
  38363. function addDependencyGroup($name, $hint)
  38364. {
  38365. $this->_isValid = 0;
  38366. $this->_packageInfo = $this->_mergeTag($this->_packageInfo,
  38367. array('attribs' => array('name' => $name, 'hint' => $hint)),
  38368. array(
  38369. 'dependencies' => array('providesextension', 'usesrole', 'usestask',
  38370. 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
  38371. 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
  38372. 'group' => array(),
  38373. ));
  38374. }
  38375. /**
  38376. * @param string package name
  38377. * @param string|false channel name, false if this is a uri
  38378. * @param string|false uri name, false if this is a channel
  38379. * @param string|false minimum version required
  38380. * @param string|false maximum version allowed
  38381. * @param string|false recommended installation version
  38382. * @param array|false versions to exclude from installation
  38383. * @param string extension this package provides, if any
  38384. * @param bool if true, tells the installer to ignore the default optional dependency group
  38385. * when installing this package
  38386. * @param bool if true, tells the installer to negate this dependency (conflicts)
  38387. * @return array
  38388. * @access private
  38389. */
  38390. function _constructDep($name, $channel, $uri, $min, $max, $recommended, $exclude,
  38391. $providesextension = false, $nodefault = false,
  38392. $conflicts = false)
  38393. {
  38394. $dep =
  38395. array(
  38396. 'name' => $name,
  38397. );
  38398. if ($channel) {
  38399. $dep['channel'] = $channel;
  38400. } elseif ($uri) {
  38401. $dep['uri'] = $uri;
  38402. }
  38403. if ($min) {
  38404. $dep['min'] = $min;
  38405. }
  38406. if ($max) {
  38407. $dep['max'] = $max;
  38408. }
  38409. if ($recommended) {
  38410. $dep['recommended'] = $recommended;
  38411. }
  38412. if ($exclude) {
  38413. if (is_array($exclude) && count($exclude) == 1) {
  38414. $exclude = $exclude[0];
  38415. }
  38416. $dep['exclude'] = $exclude;
  38417. }
  38418. if ($conflicts) {
  38419. $dep['conflicts'] = '';
  38420. }
  38421. if ($nodefault) {
  38422. $dep['nodefault'] = '';
  38423. }
  38424. if ($providesextension) {
  38425. $dep['providesextension'] = $providesextension;
  38426. }
  38427. return $dep;
  38428. }
  38429. /**
  38430. * @param package|subpackage
  38431. * @param string group name
  38432. * @param string package name
  38433. * @param string package channel
  38434. * @param string minimum version
  38435. * @param string maximum version
  38436. * @param string recommended version
  38437. * @param array|false optional excluded versions
  38438. * @param string extension this package provides, if any
  38439. * @param bool if true, tells the installer to ignore the default optional dependency group
  38440. * when installing this package
  38441. * @return bool false if the dependency group has not been initialized with
  38442. * {@link addDependencyGroup()}, or a subpackage is added with
  38443. * a providesextension
  38444. */
  38445. function addGroupPackageDepWithChannel($type, $groupname, $name, $channel, $min = false,
  38446. $max = false, $recommended = false, $exclude = false,
  38447. $providesextension = false, $nodefault = false)
  38448. {
  38449. if ($type == 'subpackage' && $providesextension) {
  38450. return false; // subpackages must be php packages
  38451. }
  38452. $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude,
  38453. $providesextension, $nodefault);
  38454. return $this->_addGroupDependency($type, $dep, $groupname);
  38455. }
  38456. /**
  38457. * @param package|subpackage
  38458. * @param string group name
  38459. * @param string package name
  38460. * @param string package uri
  38461. * @param string extension this package provides, if any
  38462. * @param bool if true, tells the installer to ignore the default optional dependency group
  38463. * when installing this package
  38464. * @return bool false if the dependency group has not been initialized with
  38465. * {@link addDependencyGroup()}
  38466. */
  38467. function addGroupPackageDepWithURI($type, $groupname, $name, $uri, $providesextension = false,
  38468. $nodefault = false)
  38469. {
  38470. if ($type == 'subpackage' && $providesextension) {
  38471. return false; // subpackages must be php packages
  38472. }
  38473. $dep = $this->_constructDep($name, false, $uri, false, false, false, false,
  38474. $providesextension, $nodefault);
  38475. return $this->_addGroupDependency($type, $dep, $groupname);
  38476. }
  38477. /**
  38478. * @param string group name (must be pre-existing)
  38479. * @param string extension name
  38480. * @param string minimum version allowed
  38481. * @param string maximum version allowed
  38482. * @param string recommended version
  38483. * @param array incompatible versions
  38484. */
  38485. function addGroupExtensionDep($groupname, $name, $min = false, $max = false,
  38486. $recommended = false, $exclude = false)
  38487. {
  38488. $this->_isValid = 0;
  38489. $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude);
  38490. return $this->_addGroupDependency('extension', $dep, $groupname);
  38491. }
  38492. /**
  38493. * @param package|subpackage|extension
  38494. * @param array dependency contents
  38495. * @param string name of the dependency group to add this to
  38496. * @return boolean
  38497. * @access private
  38498. */
  38499. function _addGroupDependency($type, $dep, $groupname)
  38500. {
  38501. $arr = array('subpackage', 'extension');
  38502. if ($type != 'package') {
  38503. array_shift($arr);
  38504. }
  38505. if ($type == 'extension') {
  38506. array_shift($arr);
  38507. }
  38508. if (!isset($this->_packageInfo['dependencies']['group'])) {
  38509. return false;
  38510. } else {
  38511. if (!isset($this->_packageInfo['dependencies']['group'][0])) {
  38512. if ($this->_packageInfo['dependencies']['group']['attribs']['name'] == $groupname) {
  38513. $this->_packageInfo['dependencies']['group'] = $this->_mergeTag(
  38514. $this->_packageInfo['dependencies']['group'], $dep,
  38515. array(
  38516. $type => $arr
  38517. ));
  38518. $this->_isValid = 0;
  38519. return true;
  38520. } else {
  38521. return false;
  38522. }
  38523. } else {
  38524. foreach ($this->_packageInfo['dependencies']['group'] as $i => $group) {
  38525. if ($group['attribs']['name'] == $groupname) {
  38526. $this->_packageInfo['dependencies']['group'][$i] = $this->_mergeTag(
  38527. $this->_packageInfo['dependencies']['group'][$i], $dep,
  38528. array(
  38529. $type => $arr
  38530. ));
  38531. $this->_isValid = 0;
  38532. return true;
  38533. }
  38534. }
  38535. return false;
  38536. }
  38537. }
  38538. }
  38539. /**
  38540. * @param optional|required
  38541. * @param string package name
  38542. * @param string package channel
  38543. * @param string minimum version
  38544. * @param string maximum version
  38545. * @param string recommended version
  38546. * @param string extension this package provides, if any
  38547. * @param bool if true, tells the installer to ignore the default optional dependency group
  38548. * when installing this package
  38549. * @param array|false optional excluded versions
  38550. */
  38551. function addPackageDepWithChannel($type, $name, $channel, $min = false, $max = false,
  38552. $recommended = false, $exclude = false,
  38553. $providesextension = false, $nodefault = false)
  38554. {
  38555. if (!in_array($type, array('optional', 'required'), true)) {
  38556. $type = 'required';
  38557. }
  38558. $this->_isValid = 0;
  38559. $arr = array('optional', 'group');
  38560. if ($type != 'required') {
  38561. array_shift($arr);
  38562. }
  38563. $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude,
  38564. $providesextension, $nodefault);
  38565. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
  38566. array(
  38567. 'dependencies' => array('providesextension', 'usesrole', 'usestask',
  38568. 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
  38569. 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
  38570. $type => $arr,
  38571. 'package' => array('subpackage', 'extension', 'os', 'arch')
  38572. ));
  38573. }
  38574. /**
  38575. * @param optional|required
  38576. * @param string name of the package
  38577. * @param string uri of the package
  38578. * @param string extension this package provides, if any
  38579. * @param bool if true, tells the installer to ignore the default optional dependency group
  38580. * when installing this package
  38581. */
  38582. function addPackageDepWithUri($type, $name, $uri, $providesextension = false,
  38583. $nodefault = false)
  38584. {
  38585. $this->_isValid = 0;
  38586. $arr = array('optional', 'group');
  38587. if ($type != 'required') {
  38588. array_shift($arr);
  38589. }
  38590. $dep = $this->_constructDep($name, false, $uri, false, false, false, false,
  38591. $providesextension, $nodefault);
  38592. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
  38593. array(
  38594. 'dependencies' => array('providesextension', 'usesrole', 'usestask',
  38595. 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
  38596. 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
  38597. $type => $arr,
  38598. 'package' => array('subpackage', 'extension', 'os', 'arch')
  38599. ));
  38600. }
  38601. /**
  38602. * @param optional|required optional, required
  38603. * @param string package name
  38604. * @param string package channel
  38605. * @param string minimum version
  38606. * @param string maximum version
  38607. * @param string recommended version
  38608. * @param array incompatible versions
  38609. * @param bool if true, tells the installer to ignore the default optional dependency group
  38610. * when installing this package
  38611. */
  38612. function addSubpackageDepWithChannel($type, $name, $channel, $min = false, $max = false,
  38613. $recommended = false, $exclude = false,
  38614. $nodefault = false)
  38615. {
  38616. $this->_isValid = 0;
  38617. $arr = array('optional', 'group');
  38618. if ($type != 'required') {
  38619. array_shift($arr);
  38620. }
  38621. $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude,
  38622. $nodefault);
  38623. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
  38624. array(
  38625. 'dependencies' => array('providesextension', 'usesrole', 'usestask',
  38626. 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
  38627. 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
  38628. $type => $arr,
  38629. 'subpackage' => array('extension', 'os', 'arch')
  38630. ));
  38631. }
  38632. /**
  38633. * @param optional|required optional, required
  38634. * @param string package name
  38635. * @param string package uri for download
  38636. * @param bool if true, tells the installer to ignore the default optional dependency group
  38637. * when installing this package
  38638. */
  38639. function addSubpackageDepWithUri($type, $name, $uri, $nodefault = false)
  38640. {
  38641. $this->_isValid = 0;
  38642. $arr = array('optional', 'group');
  38643. if ($type != 'required') {
  38644. array_shift($arr);
  38645. }
  38646. $dep = $this->_constructDep($name, false, $uri, false, false, false, false, $nodefault);
  38647. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
  38648. array(
  38649. 'dependencies' => array('providesextension', 'usesrole', 'usestask',
  38650. 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
  38651. 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
  38652. $type => $arr,
  38653. 'subpackage' => array('extension', 'os', 'arch')
  38654. ));
  38655. }
  38656. /**
  38657. * @param optional|required optional, required
  38658. * @param string extension name
  38659. * @param string minimum version
  38660. * @param string maximum version
  38661. * @param string recommended version
  38662. * @param array incompatible versions
  38663. */
  38664. function addExtensionDep($type, $name, $min = false, $max = false, $recommended = false,
  38665. $exclude = false)
  38666. {
  38667. $this->_isValid = 0;
  38668. $arr = array('optional', 'group');
  38669. if ($type != 'required') {
  38670. array_shift($arr);
  38671. }
  38672. $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude);
  38673. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
  38674. array(
  38675. 'dependencies' => array('providesextension', 'usesrole', 'usestask',
  38676. 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
  38677. 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
  38678. $type => $arr,
  38679. 'extension' => array('os', 'arch')
  38680. ));
  38681. }
  38682. /**
  38683. * @param string Operating system name
  38684. * @param boolean true if this package cannot be installed on this OS
  38685. */
  38686. function addOsDep($name, $conflicts = false)
  38687. {
  38688. $this->_isValid = 0;
  38689. $dep = array('name' => $name);
  38690. if ($conflicts) {
  38691. $dep['conflicts'] = '';
  38692. }
  38693. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
  38694. array(
  38695. 'dependencies' => array('providesextension', 'usesrole', 'usestask',
  38696. 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
  38697. 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
  38698. 'required' => array('optional', 'group'),
  38699. 'os' => array('arch')
  38700. ));
  38701. }
  38702. /**
  38703. * @param string Architecture matching pattern
  38704. * @param boolean true if this package cannot be installed on this architecture
  38705. */
  38706. function addArchDep($pattern, $conflicts = false)
  38707. {
  38708. $this->_isValid = 0;
  38709. $dep = array('pattern' => $pattern);
  38710. if ($conflicts) {
  38711. $dep['conflicts'] = '';
  38712. }
  38713. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
  38714. array(
  38715. 'dependencies' => array('providesextension', 'usesrole', 'usestask',
  38716. 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
  38717. 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
  38718. 'required' => array('optional', 'group'),
  38719. 'arch' => array()
  38720. ));
  38721. }
  38722. /**
  38723. * Set the kind of package, and erase all release tags
  38724. *
  38725. * - a php package is a PEAR-style package
  38726. * - an extbin package is a PECL-style extension binary
  38727. * - an extsrc package is a PECL-style source for a binary
  38728. * - an zendextbin package is a PECL-style zend extension binary
  38729. * - an zendextsrc package is a PECL-style source for a zend extension binary
  38730. * - a bundle package is a collection of other pre-packaged packages
  38731. * @param php|extbin|extsrc|zendextsrc|zendextbin|bundle
  38732. * @return bool success
  38733. */
  38734. function setPackageType($type)
  38735. {
  38736. $this->_isValid = 0;
  38737. if (!in_array($type, array('php', 'extbin', 'extsrc', 'zendextsrc',
  38738. 'zendextbin', 'bundle'))) {
  38739. return false;
  38740. }
  38741. if (in_array($type, array('zendextsrc', 'zendextbin'))) {
  38742. $this->_setPackageVersion2_1();
  38743. }
  38744. if ($type != 'bundle') {
  38745. $type .= 'release';
  38746. }
  38747. foreach (array('phprelease', 'extbinrelease', 'extsrcrelease',
  38748. 'zendextsrcrelease', 'zendextbinrelease', 'bundle') as $test) {
  38749. unset($this->_packageInfo[$test]);
  38750. }
  38751. if (!isset($this->_packageInfo[$type])) {
  38752. // ensure that the release tag is set up
  38753. $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('changelog'),
  38754. array(), $type);
  38755. }
  38756. $this->_packageInfo[$type] = array();
  38757. return true;
  38758. }
  38759. /**
  38760. * @return bool true if package type is set up
  38761. */
  38762. function addRelease()
  38763. {
  38764. if ($type = $this->getPackageType()) {
  38765. if ($type != 'bundle') {
  38766. $type .= 'release';
  38767. }
  38768. $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array(),
  38769. array($type => array('changelog')));
  38770. return true;
  38771. }
  38772. return false;
  38773. }
  38774. /**
  38775. * Get the current release tag in order to add to it
  38776. * @param bool returns only releases that have installcondition if true
  38777. * @return array|null
  38778. */
  38779. function &_getCurrentRelease($strict = true)
  38780. {
  38781. if ($p = $this->getPackageType()) {
  38782. if ($strict) {
  38783. if ($p == 'extsrc' || $p == 'zendextsrc') {
  38784. $a = null;
  38785. return $a;
  38786. }
  38787. }
  38788. if ($p != 'bundle') {
  38789. $p .= 'release';
  38790. }
  38791. if (isset($this->_packageInfo[$p][0])) {
  38792. return $this->_packageInfo[$p][count($this->_packageInfo[$p]) - 1];
  38793. } else {
  38794. return $this->_packageInfo[$p];
  38795. }
  38796. } else {
  38797. $a = null;
  38798. return $a;
  38799. }
  38800. }
  38801. /**
  38802. * Add a file to the current release that should be installed under a different name
  38803. * @param string <contents> path to file
  38804. * @param string name the file should be installed as
  38805. */
  38806. function addInstallAs($path, $as)
  38807. {
  38808. $r = &$this->_getCurrentRelease();
  38809. if ($r === null) {
  38810. return false;
  38811. }
  38812. $this->_isValid = 0;
  38813. $r = $this->_mergeTag($r, array('attribs' => array('name' => $path, 'as' => $as)),
  38814. array(
  38815. 'filelist' => array(),
  38816. 'install' => array('ignore')
  38817. ));
  38818. }
  38819. /**
  38820. * Add a file to the current release that should be ignored
  38821. * @param string <contents> path to file
  38822. * @return bool success of operation
  38823. */
  38824. function addIgnore($path)
  38825. {
  38826. $r = &$this->_getCurrentRelease();
  38827. if ($r === null) {
  38828. return false;
  38829. }
  38830. $this->_isValid = 0;
  38831. $r = $this->_mergeTag($r, array('attribs' => array('name' => $path)),
  38832. array(
  38833. 'filelist' => array(),
  38834. 'ignore' => array()
  38835. ));
  38836. }
  38837. /**
  38838. * Add an extension binary package for this extension source code release
  38839. *
  38840. * Note that the package must be from the same channel as the extension source package
  38841. * @param string
  38842. */
  38843. function addBinarypackage($package)
  38844. {
  38845. if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
  38846. return false;
  38847. }
  38848. $r = &$this->_getCurrentRelease(false);
  38849. if ($r === null) {
  38850. return false;
  38851. }
  38852. $this->_isValid = 0;
  38853. $r = $this->_mergeTag($r, $package,
  38854. array(
  38855. 'binarypackage' => array('filelist'),
  38856. ));
  38857. }
  38858. /**
  38859. * Add a configureoption to an extension source package
  38860. * @param string
  38861. * @param string
  38862. * @param string
  38863. */
  38864. function addConfigureOption($name, $prompt, $default = null)
  38865. {
  38866. if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
  38867. return false;
  38868. }
  38869. $r = &$this->_getCurrentRelease(false);
  38870. if ($r === null) {
  38871. return false;
  38872. }
  38873. $opt = array('attribs' => array('name' => $name, 'prompt' => $prompt));
  38874. if ($default !== null) {
  38875. $opt['attribs']['default'] = $default;
  38876. }
  38877. $this->_isValid = 0;
  38878. $r = $this->_mergeTag($r, $opt,
  38879. array(
  38880. 'configureoption' => array('binarypackage', 'filelist'),
  38881. ));
  38882. }
  38883. /**
  38884. * Set an installation condition based on php version for the current release set
  38885. * @param string minimum version
  38886. * @param string maximum version
  38887. * @param false|array incompatible versions of PHP
  38888. */
  38889. function setPhpInstallCondition($min, $max, $exclude = false)
  38890. {
  38891. $r = &$this->_getCurrentRelease();
  38892. if ($r === null) {
  38893. return false;
  38894. }
  38895. $this->_isValid = 0;
  38896. if (isset($r['installconditions']['php'])) {
  38897. unset($r['installconditions']['php']);
  38898. }
  38899. $dep = array('min' => $min, 'max' => $max);
  38900. if ($exclude) {
  38901. if (is_array($exclude) && count($exclude) == 1) {
  38902. $exclude = $exclude[0];
  38903. }
  38904. $dep['exclude'] = $exclude;
  38905. }
  38906. if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
  38907. $r = $this->_mergeTag($r, $dep,
  38908. array(
  38909. 'installconditions' => array('configureoption', 'binarypackage',
  38910. 'filelist'),
  38911. 'php' => array('extension', 'os', 'arch')
  38912. ));
  38913. } else {
  38914. $r = $this->_mergeTag($r, $dep,
  38915. array(
  38916. 'installconditions' => array('filelist'),
  38917. 'php' => array('extension', 'os', 'arch')
  38918. ));
  38919. }
  38920. }
  38921. /**
  38922. * @param optional|required optional, required
  38923. * @param string extension name
  38924. * @param string minimum version
  38925. * @param string maximum version
  38926. * @param string recommended version
  38927. * @param array incompatible versions
  38928. */
  38929. function addExtensionInstallCondition($name, $min = false, $max = false, $recommended = false,
  38930. $exclude = false)
  38931. {
  38932. $r = &$this->_getCurrentRelease();
  38933. if ($r === null) {
  38934. return false;
  38935. }
  38936. $this->_isValid = 0;
  38937. $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude);
  38938. if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
  38939. $r = $this->_mergeTag($r, $dep,
  38940. array(
  38941. 'installconditions' => array('configureoption', 'binarypackage',
  38942. 'filelist'),
  38943. 'extension' => array('os', 'arch')
  38944. ));
  38945. } else {
  38946. $r = $this->_mergeTag($r, $dep,
  38947. array(
  38948. 'installconditions' => array('filelist'),
  38949. 'extension' => array('os', 'arch')
  38950. ));
  38951. }
  38952. }
  38953. /**
  38954. * Set an installation condition based on operating system for the current release set
  38955. * @param string OS name
  38956. * @param bool whether this OS is incompatible with the current release
  38957. */
  38958. function setOsInstallCondition($name, $conflicts = false)
  38959. {
  38960. $r = &$this->_getCurrentRelease();
  38961. if ($r === null) {
  38962. return false;
  38963. }
  38964. $this->_isValid = 0;
  38965. if (isset($r['installconditions']['os'])) {
  38966. unset($r['installconditions']['os']);
  38967. }
  38968. $dep = array('name' => $name);
  38969. if ($conflicts) {
  38970. $dep['conflicts'] = '';
  38971. }
  38972. if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
  38973. $r = $this->_mergeTag($r, $dep,
  38974. array(
  38975. 'installconditions' => array('configureoption', 'binarypackage',
  38976. 'filelist'),
  38977. 'os' => array('arch')
  38978. ));
  38979. } else {
  38980. $r = $this->_mergeTag($r, $dep,
  38981. array(
  38982. 'installconditions' => array('filelist'),
  38983. 'os' => array('arch')
  38984. ));
  38985. }
  38986. }
  38987. /**
  38988. * Set an installation condition based on architecture for the current release set
  38989. * @param string architecture pattern
  38990. * @param bool whether this arch is incompatible with the current release
  38991. */
  38992. function setArchInstallCondition($pattern, $conflicts = false)
  38993. {
  38994. $r = &$this->_getCurrentRelease();
  38995. if ($r === null) {
  38996. return false;
  38997. }
  38998. $this->_isValid = 0;
  38999. if (isset($r['installconditions']['arch'])) {
  39000. unset($r['installconditions']['arch']);
  39001. }
  39002. $dep = array('pattern' => $pattern);
  39003. if ($conflicts) {
  39004. $dep['conflicts'] = '';
  39005. }
  39006. if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
  39007. $r = $this->_mergeTag($r, $dep,
  39008. array(
  39009. 'installconditions' => array('configureoption', 'binarypackage',
  39010. 'filelist'),
  39011. 'arch' => array()
  39012. ));
  39013. } else {
  39014. $r = $this->_mergeTag($r, $dep,
  39015. array(
  39016. 'installconditions' => array('filelist'),
  39017. 'arch' => array()
  39018. ));
  39019. }
  39020. }
  39021. /**
  39022. * For extension binary releases, this is used to specify either the
  39023. * static URI to a source package, or the package name and channel of the extsrc/zendextsrc
  39024. * package it is based on.
  39025. * @param string Package name, or full URI to source package (extsrc/zendextsrc type)
  39026. */
  39027. function setSourcePackage($packageOrUri)
  39028. {
  39029. $this->_isValid = 0;
  39030. if (isset($this->_packageInfo['channel'])) {
  39031. $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('phprelease',
  39032. 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
  39033. 'bundle', 'changelog'),
  39034. $packageOrUri, 'srcpackage');
  39035. } else {
  39036. $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('phprelease',
  39037. 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
  39038. 'bundle', 'changelog'), $packageOrUri, 'srcuri');
  39039. }
  39040. }
  39041. /**
  39042. * Generate a valid change log entry from the current package.xml
  39043. * @param string|false
  39044. */
  39045. function generateChangeLogEntry($notes = false)
  39046. {
  39047. return array(
  39048. 'version' =>
  39049. array(
  39050. 'release' => $this->getVersion('release'),
  39051. 'api' => $this->getVersion('api'),
  39052. ),
  39053. 'stability' =>
  39054. $this->getStability(),
  39055. 'date' => $this->getDate(),
  39056. 'license' => $this->getLicense(true),
  39057. 'notes' => $notes ? $notes : $this->getNotes()
  39058. );
  39059. }
  39060. /**
  39061. * @param string release version to set change log notes for
  39062. * @param array output of {@link generateChangeLogEntry()}
  39063. */
  39064. function setChangelogEntry($releaseversion, $contents)
  39065. {
  39066. if (!isset($this->_packageInfo['changelog'])) {
  39067. $this->_packageInfo['changelog']['release'] = $contents;
  39068. return;
  39069. }
  39070. if (!isset($this->_packageInfo['changelog']['release'][0])) {
  39071. if ($this->_packageInfo['changelog']['release']['version']['release'] == $releaseversion) {
  39072. $this->_packageInfo['changelog']['release'] = array(
  39073. $this->_packageInfo['changelog']['release']);
  39074. } else {
  39075. $this->_packageInfo['changelog']['release'] = array(
  39076. $this->_packageInfo['changelog']['release']);
  39077. return $this->_packageInfo['changelog']['release'][] = $contents;
  39078. }
  39079. }
  39080. foreach($this->_packageInfo['changelog']['release'] as $index => $changelog) {
  39081. if (isset($changelog['version']) &&
  39082. strnatcasecmp($changelog['version']['release'], $releaseversion) == 0) {
  39083. $curlog = $index;
  39084. }
  39085. }
  39086. if (isset($curlog)) {
  39087. $this->_packageInfo['changelog']['release'][$curlog] = $contents;
  39088. } else {
  39089. $this->_packageInfo['changelog']['release'][] = $contents;
  39090. }
  39091. }
  39092. /**
  39093. * Remove the changelog entirely
  39094. */
  39095. function clearChangeLog()
  39096. {
  39097. unset($this->_packageInfo['changelog']);
  39098. }
  39099. }���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/PackageFile/v2/Validator.php������������������������������������������������������0000664�0001750�0001750�00000246200�14720722517�020143� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  39100. /**
  39101. * PEAR_PackageFile_v2, package.xml version 2.0, read/write version
  39102. *
  39103. * PHP versions 4 and 5
  39104. *
  39105. * @category pear
  39106. * @package PEAR
  39107. * @author Greg Beaver <cellog@php.net>
  39108. * @copyright 1997-2009 The Authors
  39109. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  39110. * @link http://pear.php.net/package/PEAR
  39111. * @since File available since Release 1.4.0a8
  39112. */
  39113. /**
  39114. * Private validation class used by PEAR_PackageFile_v2 - do not use directly, its
  39115. * sole purpose is to split up the PEAR/PackageFile/v2.php file to make it smaller
  39116. * @category pear
  39117. * @package PEAR
  39118. * @author Greg Beaver <cellog@php.net>
  39119. * @copyright 1997-2009 The Authors
  39120. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  39121. * @version Release: 1.10.16
  39122. * @link http://pear.php.net/package/PEAR
  39123. * @since Class available since Release 1.4.0a8
  39124. * @access private
  39125. */
  39126. class PEAR_PackageFile_v2_Validator
  39127. {
  39128. /**
  39129. * @var array
  39130. */
  39131. var $_packageInfo;
  39132. /**
  39133. * @var PEAR_PackageFile_v2
  39134. */
  39135. var $_pf;
  39136. /**
  39137. * @var PEAR_ErrorStack
  39138. */
  39139. var $_stack;
  39140. /**
  39141. * @var int
  39142. */
  39143. var $_isValid = 0;
  39144. /**
  39145. * @var int
  39146. */
  39147. var $_filesValid = 0;
  39148. /**
  39149. * @var int
  39150. */
  39151. var $_curState = 0;
  39152. /**
  39153. * @param PEAR_PackageFile_v2
  39154. * @param int
  39155. */
  39156. function validate(&$pf, $state = PEAR_VALIDATE_NORMAL)
  39157. {
  39158. $this->_pf = &$pf;
  39159. $this->_curState = $state;
  39160. $this->_packageInfo = $this->_pf->getArray();
  39161. $this->_isValid = $this->_pf->_isValid;
  39162. $this->_filesValid = $this->_pf->_filesValid;
  39163. $this->_stack = &$pf->_stack;
  39164. $this->_stack->getErrors(true);
  39165. if (($this->_isValid & $state) == $state) {
  39166. return true;
  39167. }
  39168. if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
  39169. return false;
  39170. }
  39171. if (!isset($this->_packageInfo['attribs']['version']) ||
  39172. ($this->_packageInfo['attribs']['version'] != '2.0' &&
  39173. $this->_packageInfo['attribs']['version'] != '2.1')
  39174. ) {
  39175. $this->_noPackageVersion();
  39176. }
  39177. $structure =
  39178. array(
  39179. 'name',
  39180. 'channel|uri',
  39181. '*extends', // can't be multiple, but this works fine
  39182. 'summary',
  39183. 'description',
  39184. '+lead', // these all need content checks
  39185. '*developer',
  39186. '*contributor',
  39187. '*helper',
  39188. 'date',
  39189. '*time',
  39190. 'version',
  39191. 'stability',
  39192. 'license->?uri->?filesource',
  39193. 'notes',
  39194. 'contents', //special validation needed
  39195. '*compatible',
  39196. 'dependencies', //special validation needed
  39197. '*usesrole',
  39198. '*usestask', // reserve these for 1.4.0a1 to implement
  39199. // this will allow a package.xml to gracefully say it
  39200. // needs a certain package installed in order to implement a role or task
  39201. '*providesextension',
  39202. '*srcpackage|*srcuri',
  39203. '+phprelease|+extsrcrelease|+extbinrelease|' .
  39204. '+zendextsrcrelease|+zendextbinrelease|bundle', //special validation needed
  39205. '*changelog',
  39206. );
  39207. $test = $this->_packageInfo;
  39208. if (isset($test['dependencies']) &&
  39209. isset($test['dependencies']['required']) &&
  39210. isset($test['dependencies']['required']['pearinstaller']) &&
  39211. isset($test['dependencies']['required']['pearinstaller']['min']) &&
  39212. '1.10.16' != '@package' . '_version@' &&
  39213. version_compare('1.10.16',
  39214. $test['dependencies']['required']['pearinstaller']['min'], '<')
  39215. ) {
  39216. $this->_pearVersionTooLow($test['dependencies']['required']['pearinstaller']['min']);
  39217. return false;
  39218. }
  39219. // ignore post-installation array fields
  39220. if (array_key_exists('filelist', $test)) {
  39221. unset($test['filelist']);
  39222. }
  39223. if (array_key_exists('_lastmodified', $test)) {
  39224. unset($test['_lastmodified']);
  39225. }
  39226. if (array_key_exists('#binarypackage', $test)) {
  39227. unset($test['#binarypackage']);
  39228. }
  39229. if (array_key_exists('old', $test)) {
  39230. unset($test['old']);
  39231. }
  39232. if (array_key_exists('_lastversion', $test)) {
  39233. unset($test['_lastversion']);
  39234. }
  39235. if (!$this->_stupidSchemaValidate($structure, $test, '<package>')) {
  39236. return false;
  39237. }
  39238. if (empty($this->_packageInfo['name'])) {
  39239. $this->_tagCannotBeEmpty('name');
  39240. }
  39241. $test = isset($this->_packageInfo['uri']) ? 'uri' :'channel';
  39242. if (empty($this->_packageInfo[$test])) {
  39243. $this->_tagCannotBeEmpty($test);
  39244. }
  39245. if (is_array($this->_packageInfo['license']) &&
  39246. (!isset($this->_packageInfo['license']['_content']) ||
  39247. empty($this->_packageInfo['license']['_content']))) {
  39248. $this->_tagCannotBeEmpty('license');
  39249. } elseif (empty($this->_packageInfo['license'])) {
  39250. $this->_tagCannotBeEmpty('license');
  39251. }
  39252. if (empty($this->_packageInfo['summary'])) {
  39253. $this->_tagCannotBeEmpty('summary');
  39254. }
  39255. if (empty($this->_packageInfo['description'])) {
  39256. $this->_tagCannotBeEmpty('description');
  39257. }
  39258. if (empty($this->_packageInfo['date'])) {
  39259. $this->_tagCannotBeEmpty('date');
  39260. }
  39261. if (empty($this->_packageInfo['notes'])) {
  39262. $this->_tagCannotBeEmpty('notes');
  39263. }
  39264. if (isset($this->_packageInfo['time']) && empty($this->_packageInfo['time'])) {
  39265. $this->_tagCannotBeEmpty('time');
  39266. }
  39267. if (isset($this->_packageInfo['dependencies'])) {
  39268. $this->_validateDependencies();
  39269. }
  39270. if (isset($this->_packageInfo['compatible'])) {
  39271. $this->_validateCompatible();
  39272. }
  39273. if (!isset($this->_packageInfo['bundle'])) {
  39274. if (empty($this->_packageInfo['contents'])) {
  39275. $this->_tagCannotBeEmpty('contents');
  39276. }
  39277. if (!isset($this->_packageInfo['contents']['dir'])) {
  39278. $this->_filelistMustContainDir('contents');
  39279. return false;
  39280. }
  39281. if (isset($this->_packageInfo['contents']['file'])) {
  39282. $this->_filelistCannotContainFile('contents');
  39283. return false;
  39284. }
  39285. }
  39286. $this->_validateMaintainers();
  39287. $this->_validateStabilityVersion();
  39288. $fail = false;
  39289. if (array_key_exists('usesrole', $this->_packageInfo)) {
  39290. $roles = $this->_packageInfo['usesrole'];
  39291. if (!is_array($roles) || !isset($roles[0])) {
  39292. $roles = array($roles);
  39293. }
  39294. foreach ($roles as $role) {
  39295. if (!isset($role['role'])) {
  39296. $this->_usesroletaskMustHaveRoleTask('usesrole', 'role');
  39297. $fail = true;
  39298. } else {
  39299. if (!isset($role['channel'])) {
  39300. if (!isset($role['uri'])) {
  39301. $this->_usesroletaskMustHaveChannelOrUri($role['role'], 'usesrole');
  39302. $fail = true;
  39303. }
  39304. } elseif (!isset($role['package'])) {
  39305. $this->_usesroletaskMustHavePackage($role['role'], 'usesrole');
  39306. $fail = true;
  39307. }
  39308. }
  39309. }
  39310. }
  39311. if (array_key_exists('usestask', $this->_packageInfo)) {
  39312. $roles = $this->_packageInfo['usestask'];
  39313. if (!is_array($roles) || !isset($roles[0])) {
  39314. $roles = array($roles);
  39315. }
  39316. foreach ($roles as $role) {
  39317. if (!isset($role['task'])) {
  39318. $this->_usesroletaskMustHaveRoleTask('usestask', 'task');
  39319. $fail = true;
  39320. } else {
  39321. if (!isset($role['channel'])) {
  39322. if (!isset($role['uri'])) {
  39323. $this->_usesroletaskMustHaveChannelOrUri($role['task'], 'usestask');
  39324. $fail = true;
  39325. }
  39326. } elseif (!isset($role['package'])) {
  39327. $this->_usesroletaskMustHavePackage($role['task'], 'usestask');
  39328. $fail = true;
  39329. }
  39330. }
  39331. }
  39332. }
  39333. if ($fail) {
  39334. return false;
  39335. }
  39336. $list = $this->_packageInfo['contents'];
  39337. if (isset($list['dir']) && is_array($list['dir']) && isset($list['dir'][0])) {
  39338. $this->_multipleToplevelDirNotAllowed();
  39339. return $this->_isValid = 0;
  39340. }
  39341. $this->_validateFilelist();
  39342. $this->_validateRelease();
  39343. if (!$this->_stack->hasErrors()) {
  39344. $chan = $this->_pf->_registry->getChannel($this->_pf->getChannel(), true);
  39345. if (PEAR::isError($chan)) {
  39346. $this->_unknownChannel($this->_pf->getChannel());
  39347. } else {
  39348. $valpack = $chan->getValidationPackage();
  39349. // for channel validator packages, always use the default PEAR validator.
  39350. // otherwise, they can't be installed or packaged
  39351. $validator = $chan->getValidationObject($this->_pf->getPackage());
  39352. if (!$validator) {
  39353. $this->_stack->push(__FUNCTION__, 'error',
  39354. array('channel' => $chan->getName(),
  39355. 'package' => $this->_pf->getPackage(),
  39356. 'name' => $valpack['_content'],
  39357. 'version' => $valpack['attribs']['version']),
  39358. 'package "%channel%/%package%" cannot be properly validated without ' .
  39359. 'validation package "%channel%/%name%-%version%"');
  39360. return $this->_isValid = 0;
  39361. }
  39362. $validator->setPackageFile($this->_pf);
  39363. $validator->validate($state);
  39364. $failures = $validator->getFailures();
  39365. foreach ($failures['errors'] as $error) {
  39366. $this->_stack->push(__FUNCTION__, 'error', $error,
  39367. 'Channel validator error: field "%field%" - %reason%');
  39368. }
  39369. foreach ($failures['warnings'] as $warning) {
  39370. $this->_stack->push(__FUNCTION__, 'warning', $warning,
  39371. 'Channel validator warning: field "%field%" - %reason%');
  39372. }
  39373. }
  39374. }
  39375. $this->_pf->_isValid = $this->_isValid = !$this->_stack->hasErrors('error');
  39376. if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$this->_filesValid) {
  39377. if ($this->_pf->getPackageType() == 'bundle') {
  39378. if ($this->_analyzeBundledPackages()) {
  39379. $this->_filesValid = $this->_pf->_filesValid = true;
  39380. } else {
  39381. $this->_pf->_isValid = $this->_isValid = 0;
  39382. }
  39383. } else {
  39384. if (!$this->_analyzePhpFiles()) {
  39385. $this->_pf->_isValid = $this->_isValid = 0;
  39386. } else {
  39387. $this->_filesValid = $this->_pf->_filesValid = true;
  39388. }
  39389. }
  39390. }
  39391. if ($this->_isValid) {
  39392. return $this->_pf->_isValid = $this->_isValid = $state;
  39393. }
  39394. return $this->_pf->_isValid = $this->_isValid = 0;
  39395. }
  39396. function _stupidSchemaValidate($structure, $xml, $root)
  39397. {
  39398. if (!is_array($xml)) {
  39399. $xml = array();
  39400. }
  39401. $keys = array_keys($xml);
  39402. reset($keys);
  39403. $key = current($keys);
  39404. while ($key == 'attribs' || $key == '_contents') {
  39405. $key = next($keys);
  39406. }
  39407. $unfoundtags = $optionaltags = array();
  39408. $ret = true;
  39409. $mismatch = false;
  39410. foreach ($structure as $struc) {
  39411. if ($key) {
  39412. $tag = $xml[$key];
  39413. }
  39414. $test = $this->_processStructure($struc);
  39415. if (isset($test['choices'])) {
  39416. $loose = true;
  39417. foreach ($test['choices'] as $choice) {
  39418. if ($key == $choice['tag']) {
  39419. $key = next($keys);
  39420. while ($key == 'attribs' || $key == '_contents') {
  39421. $key = next($keys);
  39422. }
  39423. $unfoundtags = $optionaltags = array();
  39424. $mismatch = false;
  39425. if ($key && $key != $choice['tag'] && isset($choice['multiple'])) {
  39426. $unfoundtags[] = $choice['tag'];
  39427. $optionaltags[] = $choice['tag'];
  39428. if ($key) {
  39429. $mismatch = true;
  39430. }
  39431. }
  39432. $ret &= $this->_processAttribs($choice, $tag, $root);
  39433. continue 2;
  39434. } else {
  39435. $unfoundtags[] = $choice['tag'];
  39436. $mismatch = true;
  39437. }
  39438. if (!isset($choice['multiple']) || $choice['multiple'] != '*') {
  39439. $loose = false;
  39440. } else {
  39441. $optionaltags[] = $choice['tag'];
  39442. }
  39443. }
  39444. if (!$loose) {
  39445. $this->_invalidTagOrder($unfoundtags, $key, $root);
  39446. return false;
  39447. }
  39448. } else {
  39449. if ($key != $test['tag']) {
  39450. if (isset($test['multiple']) && $test['multiple'] != '*') {
  39451. $unfoundtags[] = $test['tag'];
  39452. $this->_invalidTagOrder($unfoundtags, $key, $root);
  39453. return false;
  39454. } else {
  39455. if ($key) {
  39456. $mismatch = true;
  39457. }
  39458. $unfoundtags[] = $test['tag'];
  39459. $optionaltags[] = $test['tag'];
  39460. }
  39461. if (!isset($test['multiple'])) {
  39462. $this->_invalidTagOrder($unfoundtags, $key, $root);
  39463. return false;
  39464. }
  39465. continue;
  39466. } else {
  39467. $unfoundtags = $optionaltags = array();
  39468. $mismatch = false;
  39469. }
  39470. $key = next($keys);
  39471. while ($key == 'attribs' || $key == '_contents') {
  39472. $key = next($keys);
  39473. }
  39474. if ($key && $key != $test['tag'] && isset($test['multiple'])) {
  39475. $unfoundtags[] = $test['tag'];
  39476. $optionaltags[] = $test['tag'];
  39477. $mismatch = true;
  39478. }
  39479. $ret &= $this->_processAttribs($test, $tag, $root);
  39480. continue;
  39481. }
  39482. }
  39483. if (!$mismatch && count($optionaltags)) {
  39484. // don't error out on any optional tags
  39485. $unfoundtags = array_diff($unfoundtags, $optionaltags);
  39486. }
  39487. if (count($unfoundtags)) {
  39488. $this->_invalidTagOrder($unfoundtags, $key, $root);
  39489. } elseif ($key) {
  39490. // unknown tags
  39491. $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
  39492. while ($key = next($keys)) {
  39493. $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
  39494. }
  39495. }
  39496. return $ret;
  39497. }
  39498. function _processAttribs($choice, $tag, $context)
  39499. {
  39500. if (isset($choice['attribs'])) {
  39501. if (!is_array($tag)) {
  39502. $tag = array($tag);
  39503. }
  39504. $tags = $tag;
  39505. if (!isset($tags[0])) {
  39506. $tags = array($tags);
  39507. }
  39508. $ret = true;
  39509. foreach ($tags as $i => $tag) {
  39510. if (!is_array($tag) || !isset($tag['attribs'])) {
  39511. foreach ($choice['attribs'] as $attrib) {
  39512. if ($attrib[0] != '?') {
  39513. $ret &= $this->_tagHasNoAttribs($choice['tag'],
  39514. $context);
  39515. continue 2;
  39516. }
  39517. }
  39518. }
  39519. foreach ($choice['attribs'] as $attrib) {
  39520. if ($attrib[0] != '?') {
  39521. if (!isset($tag['attribs'][$attrib])) {
  39522. $ret &= $this->_tagMissingAttribute($choice['tag'],
  39523. $attrib, $context);
  39524. }
  39525. }
  39526. }
  39527. }
  39528. return $ret;
  39529. }
  39530. return true;
  39531. }
  39532. function _processStructure($key)
  39533. {
  39534. $ret = array();
  39535. if (count($pieces = explode('|', $key)) > 1) {
  39536. $ret['choices'] = array();
  39537. foreach ($pieces as $piece) {
  39538. $ret['choices'][] = $this->_processStructure($piece);
  39539. }
  39540. return $ret;
  39541. }
  39542. $multi = $key[0];
  39543. if ($multi == '+' || $multi == '*') {
  39544. $ret['multiple'] = $key[0];
  39545. $key = substr($key, 1);
  39546. }
  39547. if (count($attrs = explode('->', $key)) > 1) {
  39548. $ret['tag'] = array_shift($attrs);
  39549. $ret['attribs'] = $attrs;
  39550. } else {
  39551. $ret['tag'] = $key;
  39552. }
  39553. return $ret;
  39554. }
  39555. function _validateStabilityVersion()
  39556. {
  39557. $structure = array('release', 'api');
  39558. $a = $this->_stupidSchemaValidate($structure, $this->_packageInfo['version'], '<version>');
  39559. $a &= $this->_stupidSchemaValidate($structure, $this->_packageInfo['stability'], '<stability>');
  39560. if ($a) {
  39561. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  39562. $this->_packageInfo['version']['release'])) {
  39563. $this->_invalidVersion('release', $this->_packageInfo['version']['release']);
  39564. }
  39565. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  39566. $this->_packageInfo['version']['api'])) {
  39567. $this->_invalidVersion('api', $this->_packageInfo['version']['api']);
  39568. }
  39569. if (!in_array($this->_packageInfo['stability']['release'],
  39570. array('snapshot', 'devel', 'alpha', 'beta', 'stable'))) {
  39571. $this->_invalidState('release', $this->_packageInfo['stability']['release']);
  39572. }
  39573. if (!in_array($this->_packageInfo['stability']['api'],
  39574. array('devel', 'alpha', 'beta', 'stable'))) {
  39575. $this->_invalidState('api', $this->_packageInfo['stability']['api']);
  39576. }
  39577. }
  39578. }
  39579. function _validateMaintainers()
  39580. {
  39581. $structure =
  39582. array(
  39583. 'name',
  39584. 'user',
  39585. 'email',
  39586. 'active',
  39587. );
  39588. foreach (array('lead', 'developer', 'contributor', 'helper') as $type) {
  39589. if (!isset($this->_packageInfo[$type])) {
  39590. continue;
  39591. }
  39592. if (isset($this->_packageInfo[$type][0])) {
  39593. foreach ($this->_packageInfo[$type] as $lead) {
  39594. $this->_stupidSchemaValidate($structure, $lead, '<' . $type . '>');
  39595. }
  39596. } else {
  39597. $this->_stupidSchemaValidate($structure, $this->_packageInfo[$type],
  39598. '<' . $type . '>');
  39599. }
  39600. }
  39601. }
  39602. function _validatePhpDep($dep, $installcondition = false)
  39603. {
  39604. $structure = array(
  39605. 'min',
  39606. '*max',
  39607. '*exclude',
  39608. );
  39609. $type = $installcondition ? '<installcondition><php>' : '<dependencies><required><php>';
  39610. $this->_stupidSchemaValidate($structure, $dep, $type);
  39611. if (isset($dep['min'])) {
  39612. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
  39613. $dep['min'])) {
  39614. $this->_invalidVersion($type . '<min>', $dep['min']);
  39615. }
  39616. }
  39617. if (isset($dep['max'])) {
  39618. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
  39619. $dep['max'])) {
  39620. $this->_invalidVersion($type . '<max>', $dep['max']);
  39621. }
  39622. }
  39623. if (isset($dep['exclude'])) {
  39624. if (!is_array($dep['exclude'])) {
  39625. $dep['exclude'] = array($dep['exclude']);
  39626. }
  39627. foreach ($dep['exclude'] as $exclude) {
  39628. if (!preg_match(
  39629. '/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
  39630. $exclude)) {
  39631. $this->_invalidVersion($type . '<exclude>', $exclude);
  39632. }
  39633. }
  39634. }
  39635. }
  39636. function _validatePearinstallerDep($dep)
  39637. {
  39638. $structure = array(
  39639. 'min',
  39640. '*max',
  39641. '*recommended',
  39642. '*exclude',
  39643. );
  39644. $this->_stupidSchemaValidate($structure, $dep, '<dependencies><required><pearinstaller>');
  39645. if (isset($dep['min'])) {
  39646. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  39647. $dep['min'])) {
  39648. $this->_invalidVersion('<dependencies><required><pearinstaller><min>',
  39649. $dep['min']);
  39650. }
  39651. }
  39652. if (isset($dep['max'])) {
  39653. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  39654. $dep['max'])) {
  39655. $this->_invalidVersion('<dependencies><required><pearinstaller><max>',
  39656. $dep['max']);
  39657. }
  39658. }
  39659. if (isset($dep['recommended'])) {
  39660. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  39661. $dep['recommended'])) {
  39662. $this->_invalidVersion('<dependencies><required><pearinstaller><recommended>',
  39663. $dep['recommended']);
  39664. }
  39665. }
  39666. if (isset($dep['exclude'])) {
  39667. if (!is_array($dep['exclude'])) {
  39668. $dep['exclude'] = array($dep['exclude']);
  39669. }
  39670. foreach ($dep['exclude'] as $exclude) {
  39671. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  39672. $exclude)) {
  39673. $this->_invalidVersion('<dependencies><required><pearinstaller><exclude>',
  39674. $exclude);
  39675. }
  39676. }
  39677. }
  39678. }
  39679. function _validatePackageDep($dep, $group, $type = '<package>')
  39680. {
  39681. if (isset($dep['uri'])) {
  39682. if (isset($dep['conflicts'])) {
  39683. $structure = array(
  39684. 'name',
  39685. 'uri',
  39686. 'conflicts',
  39687. '*providesextension',
  39688. );
  39689. } else {
  39690. $structure = array(
  39691. 'name',
  39692. 'uri',
  39693. '*providesextension',
  39694. );
  39695. }
  39696. } else {
  39697. if (isset($dep['conflicts'])) {
  39698. $structure = array(
  39699. 'name',
  39700. 'channel',
  39701. '*min',
  39702. '*max',
  39703. '*exclude',
  39704. 'conflicts',
  39705. '*providesextension',
  39706. );
  39707. } else {
  39708. $structure = array(
  39709. 'name',
  39710. 'channel',
  39711. '*min',
  39712. '*max',
  39713. '*recommended',
  39714. '*exclude',
  39715. '*nodefault',
  39716. '*providesextension',
  39717. );
  39718. }
  39719. }
  39720. if (isset($dep['name'])) {
  39721. $type .= '<name>' . $dep['name'] . '</name>';
  39722. }
  39723. $this->_stupidSchemaValidate($structure, $dep, '<dependencies>' . $group . $type);
  39724. if (isset($dep['uri']) && (isset($dep['min']) || isset($dep['max']) ||
  39725. isset($dep['recommended']) || isset($dep['exclude']))) {
  39726. $this->_uriDepsCannotHaveVersioning('<dependencies>' . $group . $type);
  39727. }
  39728. if (isset($dep['channel']) && strtolower($dep['channel']) == '__uri') {
  39729. $this->_DepchannelCannotBeUri('<dependencies>' . $group . $type);
  39730. }
  39731. if (isset($dep['min'])) {
  39732. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  39733. $dep['min'])) {
  39734. $this->_invalidVersion('<dependencies>' . $group . $type . '<min>', $dep['min']);
  39735. }
  39736. }
  39737. if (isset($dep['max'])) {
  39738. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  39739. $dep['max'])) {
  39740. $this->_invalidVersion('<dependencies>' . $group . $type . '<max>', $dep['max']);
  39741. }
  39742. }
  39743. if (isset($dep['recommended'])) {
  39744. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  39745. $dep['recommended'])) {
  39746. $this->_invalidVersion('<dependencies>' . $group . $type . '<recommended>',
  39747. $dep['recommended']);
  39748. }
  39749. }
  39750. if (isset($dep['exclude'])) {
  39751. if (!is_array($dep['exclude'])) {
  39752. $dep['exclude'] = array($dep['exclude']);
  39753. }
  39754. foreach ($dep['exclude'] as $exclude) {
  39755. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  39756. $exclude)) {
  39757. $this->_invalidVersion('<dependencies>' . $group . $type . '<exclude>',
  39758. $exclude);
  39759. }
  39760. }
  39761. }
  39762. }
  39763. function _validateSubpackageDep($dep, $group)
  39764. {
  39765. $this->_validatePackageDep($dep, $group, '<subpackage>');
  39766. if (isset($dep['providesextension'])) {
  39767. $this->_subpackageCannotProvideExtension(isset($dep['name']) ? $dep['name'] : '');
  39768. }
  39769. if (isset($dep['conflicts'])) {
  39770. $this->_subpackagesCannotConflict(isset($dep['name']) ? $dep['name'] : '');
  39771. }
  39772. }
  39773. function _validateExtensionDep($dep, $group = false, $installcondition = false)
  39774. {
  39775. if (isset($dep['conflicts'])) {
  39776. $structure = array(
  39777. 'name',
  39778. '*min',
  39779. '*max',
  39780. '*exclude',
  39781. 'conflicts',
  39782. );
  39783. } else {
  39784. $structure = array(
  39785. 'name',
  39786. '*min',
  39787. '*max',
  39788. '*recommended',
  39789. '*exclude',
  39790. );
  39791. }
  39792. if ($installcondition) {
  39793. $type = '<installcondition><extension>';
  39794. } else {
  39795. $type = '<dependencies>' . $group . '<extension>';
  39796. }
  39797. if (isset($dep['name'])) {
  39798. $type .= '<name>' . $dep['name'] . '</name>';
  39799. }
  39800. $this->_stupidSchemaValidate($structure, $dep, $type);
  39801. if (isset($dep['min'])) {
  39802. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  39803. $dep['min'])) {
  39804. $this->_invalidVersion(substr($type, 1) . '<min', $dep['min']);
  39805. }
  39806. }
  39807. if (isset($dep['max'])) {
  39808. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  39809. $dep['max'])) {
  39810. $this->_invalidVersion(substr($type, 1) . '<max', $dep['max']);
  39811. }
  39812. }
  39813. if (isset($dep['recommended'])) {
  39814. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  39815. $dep['recommended'])) {
  39816. $this->_invalidVersion(substr($type, 1) . '<recommended', $dep['recommended']);
  39817. }
  39818. }
  39819. if (isset($dep['exclude'])) {
  39820. if (!is_array($dep['exclude'])) {
  39821. $dep['exclude'] = array($dep['exclude']);
  39822. }
  39823. foreach ($dep['exclude'] as $exclude) {
  39824. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  39825. $exclude)) {
  39826. $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
  39827. }
  39828. }
  39829. }
  39830. }
  39831. function _validateOsDep($dep, $installcondition = false)
  39832. {
  39833. $structure = array(
  39834. 'name',
  39835. '*conflicts',
  39836. );
  39837. $type = $installcondition ? '<installcondition><os>' : '<dependencies><required><os>';
  39838. if ($this->_stupidSchemaValidate($structure, $dep, $type)) {
  39839. if ($dep['name'] == '*') {
  39840. if (array_key_exists('conflicts', $dep)) {
  39841. $this->_cannotConflictWithAllOs($type);
  39842. }
  39843. }
  39844. }
  39845. }
  39846. function _validateArchDep($dep, $installcondition = false)
  39847. {
  39848. $structure = array(
  39849. 'pattern',
  39850. '*conflicts',
  39851. );
  39852. $type = $installcondition ? '<installcondition><arch>' : '<dependencies><required><arch>';
  39853. $this->_stupidSchemaValidate($structure, $dep, $type);
  39854. }
  39855. function _validateInstallConditions($cond, $release)
  39856. {
  39857. $structure = array(
  39858. '*php',
  39859. '*extension',
  39860. '*os',
  39861. '*arch',
  39862. );
  39863. if (!$this->_stupidSchemaValidate($structure,
  39864. $cond, $release)) {
  39865. return false;
  39866. }
  39867. foreach (array('php', 'extension', 'os', 'arch') as $type) {
  39868. if (isset($cond[$type])) {
  39869. $iter = $cond[$type];
  39870. if (!is_array($iter) || !isset($iter[0])) {
  39871. $iter = array($iter);
  39872. }
  39873. foreach ($iter as $package) {
  39874. if ($type == 'extension') {
  39875. $this->{"_validate{$type}Dep"}($package, false, true);
  39876. } else {
  39877. $this->{"_validate{$type}Dep"}($package, true);
  39878. }
  39879. }
  39880. }
  39881. }
  39882. }
  39883. function _validateDependencies()
  39884. {
  39885. $structure = array(
  39886. 'required',
  39887. '*optional',
  39888. '*group->name->hint'
  39889. );
  39890. if (!$this->_stupidSchemaValidate($structure,
  39891. $this->_packageInfo['dependencies'], '<dependencies>')) {
  39892. return false;
  39893. }
  39894. foreach (array('required', 'optional') as $simpledep) {
  39895. if (isset($this->_packageInfo['dependencies'][$simpledep])) {
  39896. if ($simpledep == 'optional') {
  39897. $structure = array(
  39898. '*package',
  39899. '*subpackage',
  39900. '*extension',
  39901. );
  39902. } else {
  39903. $structure = array(
  39904. 'php',
  39905. 'pearinstaller',
  39906. '*package',
  39907. '*subpackage',
  39908. '*extension',
  39909. '*os',
  39910. '*arch',
  39911. );
  39912. }
  39913. if ($this->_stupidSchemaValidate($structure,
  39914. $this->_packageInfo['dependencies'][$simpledep],
  39915. "<dependencies><$simpledep>")) {
  39916. foreach (array('package', 'subpackage', 'extension') as $type) {
  39917. if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
  39918. $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
  39919. if (!isset($iter[0])) {
  39920. $iter = array($iter);
  39921. }
  39922. foreach ($iter as $package) {
  39923. if ($type != 'extension') {
  39924. if (isset($package['uri'])) {
  39925. if (isset($package['channel'])) {
  39926. $this->_UrlOrChannel($type,
  39927. $package['name']);
  39928. }
  39929. } else {
  39930. if (!isset($package['channel'])) {
  39931. $this->_NoChannel($type, $package['name']);
  39932. }
  39933. }
  39934. }
  39935. $this->{"_validate{$type}Dep"}($package, "<$simpledep>");
  39936. }
  39937. }
  39938. }
  39939. if ($simpledep == 'optional') {
  39940. continue;
  39941. }
  39942. foreach (array('php', 'pearinstaller', 'os', 'arch') as $type) {
  39943. if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
  39944. $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
  39945. if (!isset($iter[0])) {
  39946. $iter = array($iter);
  39947. }
  39948. foreach ($iter as $package) {
  39949. $this->{"_validate{$type}Dep"}($package);
  39950. }
  39951. }
  39952. }
  39953. }
  39954. }
  39955. }
  39956. if (isset($this->_packageInfo['dependencies']['group'])) {
  39957. $groups = $this->_packageInfo['dependencies']['group'];
  39958. if (!isset($groups[0])) {
  39959. $groups = array($groups);
  39960. }
  39961. $structure = array(
  39962. '*package',
  39963. '*subpackage',
  39964. '*extension',
  39965. );
  39966. foreach ($groups as $group) {
  39967. if ($this->_stupidSchemaValidate($structure, $group, '<group>')) {
  39968. if (!PEAR_Validate::validGroupName($group['attribs']['name'])) {
  39969. $this->_invalidDepGroupName($group['attribs']['name']);
  39970. }
  39971. foreach (array('package', 'subpackage', 'extension') as $type) {
  39972. if (isset($group[$type])) {
  39973. $iter = $group[$type];
  39974. if (!isset($iter[0])) {
  39975. $iter = array($iter);
  39976. }
  39977. foreach ($iter as $package) {
  39978. if ($type != 'extension') {
  39979. if (isset($package['uri'])) {
  39980. if (isset($package['channel'])) {
  39981. $this->_UrlOrChannelGroup($type,
  39982. $package['name'],
  39983. $group['name']);
  39984. }
  39985. } else {
  39986. if (!isset($package['channel'])) {
  39987. $this->_NoChannelGroup($type,
  39988. $package['name'],
  39989. $group['name']);
  39990. }
  39991. }
  39992. }
  39993. $this->{"_validate{$type}Dep"}($package, '<group name="' .
  39994. $group['attribs']['name'] . '">');
  39995. }
  39996. }
  39997. }
  39998. }
  39999. }
  40000. }
  40001. }
  40002. function _validateCompatible()
  40003. {
  40004. $compat = $this->_packageInfo['compatible'];
  40005. if (!isset($compat[0])) {
  40006. $compat = array($compat);
  40007. }
  40008. $required = array('name', 'channel', 'min', 'max', '*exclude');
  40009. foreach ($compat as $package) {
  40010. $type = '<compatible>';
  40011. if (is_array($package) && array_key_exists('name', $package)) {
  40012. $type .= '<name>' . $package['name'] . '</name>';
  40013. }
  40014. $this->_stupidSchemaValidate($required, $package, $type);
  40015. if (is_array($package) && array_key_exists('min', $package)) {
  40016. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  40017. $package['min'])) {
  40018. $this->_invalidVersion(substr($type, 1) . '<min', $package['min']);
  40019. }
  40020. }
  40021. if (is_array($package) && array_key_exists('max', $package)) {
  40022. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  40023. $package['max'])) {
  40024. $this->_invalidVersion(substr($type, 1) . '<max', $package['max']);
  40025. }
  40026. }
  40027. if (is_array($package) && array_key_exists('exclude', $package)) {
  40028. if (!is_array($package['exclude'])) {
  40029. $package['exclude'] = array($package['exclude']);
  40030. }
  40031. foreach ($package['exclude'] as $exclude) {
  40032. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  40033. $exclude)) {
  40034. $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
  40035. }
  40036. }
  40037. }
  40038. }
  40039. }
  40040. function _validateBundle($list)
  40041. {
  40042. if (!is_array($list) || !isset($list['bundledpackage'])) {
  40043. return $this->_NoBundledPackages();
  40044. }
  40045. if (!is_array($list['bundledpackage']) || !isset($list['bundledpackage'][0])) {
  40046. return $this->_AtLeast2BundledPackages();
  40047. }
  40048. foreach ($list['bundledpackage'] as $package) {
  40049. if (!is_string($package)) {
  40050. $this->_bundledPackagesMustBeFilename();
  40051. }
  40052. }
  40053. }
  40054. function _validateFilelist($list = false, $allowignore = false, $dirs = '')
  40055. {
  40056. $iscontents = false;
  40057. if (!$list) {
  40058. $iscontents = true;
  40059. $list = $this->_packageInfo['contents'];
  40060. if (isset($this->_packageInfo['bundle'])) {
  40061. return $this->_validateBundle($list);
  40062. }
  40063. }
  40064. if ($allowignore) {
  40065. $struc = array(
  40066. '*install->name->as',
  40067. '*ignore->name'
  40068. );
  40069. } else {
  40070. $struc = array(
  40071. '*dir->name->?baseinstalldir',
  40072. '*file->name->role->?baseinstalldir->?md5sum'
  40073. );
  40074. if (isset($list['dir']) && isset($list['file'])) {
  40075. // stave off validation errors without requiring a set order.
  40076. $_old = $list;
  40077. if (isset($list['attribs'])) {
  40078. $list = array('attribs' => $_old['attribs']);
  40079. }
  40080. $list['dir'] = $_old['dir'];
  40081. $list['file'] = $_old['file'];
  40082. }
  40083. }
  40084. if (!isset($list['attribs']) || !isset($list['attribs']['name'])) {
  40085. $unknown = $allowignore ? '<filelist>' : '<dir name="*unknown*">';
  40086. $dirname = $iscontents ? '<contents>' : $unknown;
  40087. } else {
  40088. $dirname = '<dir name="' . $list['attribs']['name'] . '">';
  40089. if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
  40090. str_replace('\\', '/', $list['attribs']['name']))) {
  40091. // file contains .. parent directory or . cur directory
  40092. $this->_invalidDirName($list['attribs']['name']);
  40093. }
  40094. }
  40095. $res = $this->_stupidSchemaValidate($struc, $list, $dirname);
  40096. if ($allowignore && $res) {
  40097. $ignored_or_installed = array();
  40098. $this->_pf->getFilelist();
  40099. $fcontents = $this->_pf->getContents();
  40100. $filelist = array();
  40101. if (!isset($fcontents['dir']['file'][0])) {
  40102. $fcontents['dir']['file'] = array($fcontents['dir']['file']);
  40103. }
  40104. foreach ($fcontents['dir']['file'] as $file) {
  40105. $filelist[$file['attribs']['name']] = true;
  40106. }
  40107. if (isset($list['install'])) {
  40108. if (!isset($list['install'][0])) {
  40109. $list['install'] = array($list['install']);
  40110. }
  40111. foreach ($list['install'] as $file) {
  40112. if (!isset($filelist[$file['attribs']['name']])) {
  40113. $this->_notInContents($file['attribs']['name'], 'install');
  40114. continue;
  40115. }
  40116. if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) {
  40117. $this->_multipleInstallAs($file['attribs']['name']);
  40118. }
  40119. if (!isset($ignored_or_installed[$file['attribs']['name']])) {
  40120. $ignored_or_installed[$file['attribs']['name']] = array();
  40121. }
  40122. $ignored_or_installed[$file['attribs']['name']][] = 1;
  40123. if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
  40124. str_replace('\\', '/', $file['attribs']['as']))) {
  40125. // file contains .. parent directory or . cur directory references
  40126. $this->_invalidFileInstallAs($file['attribs']['name'],
  40127. $file['attribs']['as']);
  40128. }
  40129. }
  40130. }
  40131. if (isset($list['ignore'])) {
  40132. if (!isset($list['ignore'][0])) {
  40133. $list['ignore'] = array($list['ignore']);
  40134. }
  40135. foreach ($list['ignore'] as $file) {
  40136. if (!isset($filelist[$file['attribs']['name']])) {
  40137. $this->_notInContents($file['attribs']['name'], 'ignore');
  40138. continue;
  40139. }
  40140. if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) {
  40141. $this->_ignoreAndInstallAs($file['attribs']['name']);
  40142. }
  40143. }
  40144. }
  40145. }
  40146. if (!$allowignore && isset($list['file'])) {
  40147. if (is_string($list['file'])) {
  40148. $this->_oldStyleFileNotAllowed();
  40149. return false;
  40150. }
  40151. if (!isset($list['file'][0])) {
  40152. // single file
  40153. $list['file'] = array($list['file']);
  40154. }
  40155. foreach ($list['file'] as $i => $file)
  40156. {
  40157. if (isset($file['attribs']) && isset($file['attribs']['name'])) {
  40158. if ($file['attribs']['name'][0] == '.' &&
  40159. $file['attribs']['name'][1] == '/') {
  40160. // name is something like "./doc/whatever.txt"
  40161. $this->_invalidFileName($file['attribs']['name'], $dirname);
  40162. }
  40163. if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
  40164. str_replace('\\', '/', $file['attribs']['name']))) {
  40165. // file contains .. parent directory or . cur directory
  40166. $this->_invalidFileName($file['attribs']['name'], $dirname);
  40167. }
  40168. }
  40169. if (isset($file['attribs']) && isset($file['attribs']['role'])) {
  40170. if (!$this->_validateRole($file['attribs']['role'])) {
  40171. if (isset($this->_packageInfo['usesrole'])) {
  40172. $roles = $this->_packageInfo['usesrole'];
  40173. if (!isset($roles[0])) {
  40174. $roles = array($roles);
  40175. }
  40176. foreach ($roles as $role) {
  40177. if ($role['role'] = $file['attribs']['role']) {
  40178. $msg = 'This package contains role "%role%" and requires ' .
  40179. 'package "%package%" to be used';
  40180. if (isset($role['uri'])) {
  40181. $params = array('role' => $role['role'],
  40182. 'package' => $role['uri']);
  40183. } else {
  40184. $params = array('role' => $role['role'],
  40185. 'package' => $this->_pf->_registry->
  40186. parsedPackageNameToString(array('package' =>
  40187. $role['package'], 'channel' => $role['channel']),
  40188. true));
  40189. }
  40190. $this->_stack->push('_mustInstallRole', 'error', $params, $msg);
  40191. }
  40192. }
  40193. }
  40194. $this->_invalidFileRole($file['attribs']['name'],
  40195. $dirname, $file['attribs']['role']);
  40196. }
  40197. }
  40198. if (!isset($file['attribs'])) {
  40199. continue;
  40200. }
  40201. $save = $file['attribs'];
  40202. if ($dirs) {
  40203. $save['name'] = $dirs . '/' . $save['name'];
  40204. }
  40205. unset($file['attribs']);
  40206. if (count($file) && $this->_curState != PEAR_VALIDATE_DOWNLOADING) { // has tasks
  40207. foreach ($file as $task => $value) {
  40208. if ($tagClass = $this->_pf->getTask($task)) {
  40209. if (!is_array($value) || !isset($value[0])) {
  40210. $value = array($value);
  40211. }
  40212. foreach ($value as $v) {
  40213. $ret = call_user_func(array($tagClass, 'validateXml'),
  40214. $this->_pf, $v, $this->_pf->_config, $save);
  40215. if (is_array($ret)) {
  40216. $this->_invalidTask($task, $ret, isset($save['name']) ?
  40217. $save['name'] : '');
  40218. }
  40219. }
  40220. } else {
  40221. if (isset($this->_packageInfo['usestask'])) {
  40222. $roles = $this->_packageInfo['usestask'];
  40223. if (!isset($roles[0])) {
  40224. $roles = array($roles);
  40225. }
  40226. foreach ($roles as $role) {
  40227. if ($role['task'] = $task) {
  40228. $msg = 'This package contains task "%task%" and requires ' .
  40229. 'package "%package%" to be used';
  40230. if (isset($role['uri'])) {
  40231. $params = array('task' => $role['task'],
  40232. 'package' => $role['uri']);
  40233. } else {
  40234. $params = array('task' => $role['task'],
  40235. 'package' => $this->_pf->_registry->
  40236. parsedPackageNameToString(array('package' =>
  40237. $role['package'], 'channel' => $role['channel']),
  40238. true));
  40239. }
  40240. $this->_stack->push('_mustInstallTask', 'error',
  40241. $params, $msg);
  40242. }
  40243. }
  40244. }
  40245. $this->_unknownTask($task, $save['name']);
  40246. }
  40247. }
  40248. }
  40249. }
  40250. }
  40251. if (isset($list['ignore'])) {
  40252. if (!$allowignore) {
  40253. $this->_ignoreNotAllowed('ignore');
  40254. }
  40255. }
  40256. if (isset($list['install'])) {
  40257. if (!$allowignore) {
  40258. $this->_ignoreNotAllowed('install');
  40259. }
  40260. }
  40261. if (isset($list['file'])) {
  40262. if ($allowignore) {
  40263. $this->_fileNotAllowed('file');
  40264. }
  40265. }
  40266. if (isset($list['dir'])) {
  40267. if ($allowignore) {
  40268. $this->_fileNotAllowed('dir');
  40269. } else {
  40270. if (!isset($list['dir'][0])) {
  40271. $list['dir'] = array($list['dir']);
  40272. }
  40273. foreach ($list['dir'] as $dir) {
  40274. if (isset($dir['attribs']) && isset($dir['attribs']['name'])) {
  40275. if ($dir['attribs']['name'] == '/' ||
  40276. !isset($this->_packageInfo['contents']['dir']['dir'])) {
  40277. // always use nothing if the filelist has already been flattened
  40278. $newdirs = '';
  40279. } elseif ($dirs == '') {
  40280. $newdirs = $dir['attribs']['name'];
  40281. } else {
  40282. $newdirs = $dirs . '/' . $dir['attribs']['name'];
  40283. }
  40284. } else {
  40285. $newdirs = $dirs;
  40286. }
  40287. $this->_validateFilelist($dir, $allowignore, $newdirs);
  40288. }
  40289. }
  40290. }
  40291. }
  40292. function _validateRelease()
  40293. {
  40294. if (isset($this->_packageInfo['phprelease'])) {
  40295. $release = 'phprelease';
  40296. if (isset($this->_packageInfo['providesextension'])) {
  40297. $this->_cannotProvideExtension($release);
  40298. }
  40299. if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
  40300. $this->_cannotHaveSrcpackage($release);
  40301. }
  40302. $releases = $this->_packageInfo['phprelease'];
  40303. if (!is_array($releases)) {
  40304. return true;
  40305. }
  40306. if (!isset($releases[0])) {
  40307. $releases = array($releases);
  40308. }
  40309. foreach ($releases as $rel) {
  40310. $this->_stupidSchemaValidate(array(
  40311. '*installconditions',
  40312. '*filelist',
  40313. ), $rel, '<phprelease>');
  40314. }
  40315. }
  40316. foreach (array('', 'zend') as $prefix) {
  40317. $releasetype = $prefix . 'extsrcrelease';
  40318. if (isset($this->_packageInfo[$releasetype])) {
  40319. $release = $releasetype;
  40320. if (!isset($this->_packageInfo['providesextension'])) {
  40321. $this->_mustProvideExtension($release);
  40322. }
  40323. if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
  40324. $this->_cannotHaveSrcpackage($release);
  40325. }
  40326. $releases = $this->_packageInfo[$releasetype];
  40327. if (!is_array($releases)) {
  40328. return true;
  40329. }
  40330. if (!isset($releases[0])) {
  40331. $releases = array($releases);
  40332. }
  40333. foreach ($releases as $rel) {
  40334. $this->_stupidSchemaValidate(array(
  40335. '*installconditions',
  40336. '*configureoption->name->prompt->?default',
  40337. '*binarypackage',
  40338. '*filelist',
  40339. ), $rel, '<' . $releasetype . '>');
  40340. if (isset($rel['binarypackage'])) {
  40341. if (!is_array($rel['binarypackage']) || !isset($rel['binarypackage'][0])) {
  40342. $rel['binarypackage'] = array($rel['binarypackage']);
  40343. }
  40344. foreach ($rel['binarypackage'] as $bin) {
  40345. if (!is_string($bin)) {
  40346. $this->_binaryPackageMustBePackagename();
  40347. }
  40348. }
  40349. }
  40350. }
  40351. }
  40352. $releasetype = 'extbinrelease';
  40353. if (isset($this->_packageInfo[$releasetype])) {
  40354. $release = $releasetype;
  40355. if (!isset($this->_packageInfo['providesextension'])) {
  40356. $this->_mustProvideExtension($release);
  40357. }
  40358. if (isset($this->_packageInfo['channel']) &&
  40359. !isset($this->_packageInfo['srcpackage'])) {
  40360. $this->_mustSrcPackage($release);
  40361. }
  40362. if (isset($this->_packageInfo['uri']) && !isset($this->_packageInfo['srcuri'])) {
  40363. $this->_mustSrcuri($release);
  40364. }
  40365. $releases = $this->_packageInfo[$releasetype];
  40366. if (!is_array($releases)) {
  40367. return true;
  40368. }
  40369. if (!isset($releases[0])) {
  40370. $releases = array($releases);
  40371. }
  40372. foreach ($releases as $rel) {
  40373. $this->_stupidSchemaValidate(array(
  40374. '*installconditions',
  40375. '*filelist',
  40376. ), $rel, '<' . $releasetype . '>');
  40377. }
  40378. }
  40379. }
  40380. if (isset($this->_packageInfo['bundle'])) {
  40381. $release = 'bundle';
  40382. if (isset($this->_packageInfo['providesextension'])) {
  40383. $this->_cannotProvideExtension($release);
  40384. }
  40385. if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
  40386. $this->_cannotHaveSrcpackage($release);
  40387. }
  40388. $releases = $this->_packageInfo['bundle'];
  40389. if (!is_array($releases) || !isset($releases[0])) {
  40390. $releases = array($releases);
  40391. }
  40392. foreach ($releases as $rel) {
  40393. $this->_stupidSchemaValidate(array(
  40394. '*installconditions',
  40395. '*filelist',
  40396. ), $rel, '<bundle>');
  40397. }
  40398. }
  40399. foreach ($releases as $rel) {
  40400. if (is_array($rel) && array_key_exists('installconditions', $rel)) {
  40401. $this->_validateInstallConditions($rel['installconditions'],
  40402. "<$release><installconditions>");
  40403. }
  40404. if (is_array($rel) && array_key_exists('filelist', $rel)) {
  40405. if ($rel['filelist']) {
  40406. $this->_validateFilelist($rel['filelist'], true);
  40407. }
  40408. }
  40409. }
  40410. }
  40411. /**
  40412. * This is here to allow role extension through plugins
  40413. * @param string
  40414. */
  40415. function _validateRole($role)
  40416. {
  40417. return in_array($role, PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType()));
  40418. }
  40419. function _pearVersionTooLow($version)
  40420. {
  40421. $this->_stack->push(__FUNCTION__, 'error',
  40422. array('version' => $version),
  40423. 'This package.xml requires PEAR version %version% to parse properly, we are ' .
  40424. 'version 1.10.16');
  40425. }
  40426. function _invalidTagOrder($oktags, $actual, $root)
  40427. {
  40428. $this->_stack->push(__FUNCTION__, 'error',
  40429. array('oktags' => $oktags, 'actual' => $actual, 'root' => $root),
  40430. 'Invalid tag order in %root%, found <%actual%> expected one of "%oktags%"');
  40431. }
  40432. function _ignoreNotAllowed($type)
  40433. {
  40434. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
  40435. '<%type%> is not allowed inside global <contents>, only inside ' .
  40436. '<phprelease>/<extbinrelease>/<zendextbinrelease>, use <dir> and <file> only');
  40437. }
  40438. function _fileNotAllowed($type)
  40439. {
  40440. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
  40441. '<%type%> is not allowed inside release <filelist>, only inside ' .
  40442. '<contents>, use <ignore> and <install> only');
  40443. }
  40444. function _oldStyleFileNotAllowed()
  40445. {
  40446. $this->_stack->push(__FUNCTION__, 'error', array(),
  40447. 'Old-style <file>name</file> is not allowed. Use' .
  40448. '<file name="name" role="role"/>');
  40449. }
  40450. function _tagMissingAttribute($tag, $attr, $context)
  40451. {
  40452. $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag,
  40453. 'attribute' => $attr, 'context' => $context),
  40454. 'tag <%tag%> in context "%context%" has no attribute "%attribute%"');
  40455. }
  40456. function _tagHasNoAttribs($tag, $context)
  40457. {
  40458. $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag,
  40459. 'context' => $context),
  40460. 'tag <%tag%> has no attributes in context "%context%"');
  40461. }
  40462. function _invalidInternalStructure()
  40463. {
  40464. $this->_stack->push(__FUNCTION__, 'exception', array(),
  40465. 'internal array was not generated by compatible parser, or extreme parser error, cannot continue');
  40466. }
  40467. function _invalidFileRole($file, $dir, $role)
  40468. {
  40469. $this->_stack->push(__FUNCTION__, 'error', array(
  40470. 'file' => $file, 'dir' => $dir, 'role' => $role,
  40471. 'roles' => PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType())),
  40472. 'File "%file%" in directory "%dir%" has invalid role "%role%", should be one of %roles%');
  40473. }
  40474. function _invalidFileName($file, $dir)
  40475. {
  40476. $this->_stack->push(__FUNCTION__, 'error', array(
  40477. 'file' => $file),
  40478. 'File "%file%" in directory "%dir%" cannot begin with "./" or contain ".."');
  40479. }
  40480. function _invalidFileInstallAs($file, $as)
  40481. {
  40482. $this->_stack->push(__FUNCTION__, 'error', array(
  40483. 'file' => $file, 'as' => $as),
  40484. 'File "%file%" <install as="%as%"/> cannot contain "./" or contain ".."');
  40485. }
  40486. function _invalidDirName($dir)
  40487. {
  40488. $this->_stack->push(__FUNCTION__, 'error', array(
  40489. 'dir' => $file),
  40490. 'Directory "%dir%" cannot begin with "./" or contain ".."');
  40491. }
  40492. function _filelistCannotContainFile($filelist)
  40493. {
  40494. $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist),
  40495. '<%tag%> can only contain <dir>, contains <file>. Use ' .
  40496. '<dir name="/"> as the first dir element');
  40497. }
  40498. function _filelistMustContainDir($filelist)
  40499. {
  40500. $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist),
  40501. '<%tag%> must contain <dir>. Use <dir name="/"> as the ' .
  40502. 'first dir element');
  40503. }
  40504. function _tagCannotBeEmpty($tag)
  40505. {
  40506. $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag),
  40507. '<%tag%> cannot be empty (<%tag%/>)');
  40508. }
  40509. function _UrlOrChannel($type, $name)
  40510. {
  40511. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
  40512. 'name' => $name),
  40513. 'Required dependency <%type%> "%name%" can have either url OR ' .
  40514. 'channel attributes, and not both');
  40515. }
  40516. function _NoChannel($type, $name)
  40517. {
  40518. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
  40519. 'name' => $name),
  40520. 'Required dependency <%type%> "%name%" must have either url OR ' .
  40521. 'channel attributes');
  40522. }
  40523. function _UrlOrChannelGroup($type, $name, $group)
  40524. {
  40525. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
  40526. 'name' => $name, 'group' => $group),
  40527. 'Group "%group%" dependency <%type%> "%name%" can have either url OR ' .
  40528. 'channel attributes, and not both');
  40529. }
  40530. function _NoChannelGroup($type, $name, $group)
  40531. {
  40532. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
  40533. 'name' => $name, 'group' => $group),
  40534. 'Group "%group%" dependency <%type%> "%name%" must have either url OR ' .
  40535. 'channel attributes');
  40536. }
  40537. function _unknownChannel($channel)
  40538. {
  40539. $this->_stack->push(__FUNCTION__, 'error', array('channel' => $channel),
  40540. 'Unknown channel "%channel%"');
  40541. }
  40542. function _noPackageVersion()
  40543. {
  40544. $this->_stack->push(__FUNCTION__, 'error', array(),
  40545. 'package.xml <package> tag has no version attribute, or version is not 2.0');
  40546. }
  40547. function _NoBundledPackages()
  40548. {
  40549. $this->_stack->push(__FUNCTION__, 'error', array(),
  40550. 'No <bundledpackage> tag was found in <contents>, required for bundle packages');
  40551. }
  40552. function _AtLeast2BundledPackages()
  40553. {
  40554. $this->_stack->push(__FUNCTION__, 'error', array(),
  40555. 'At least 2 packages must be bundled in a bundle package');
  40556. }
  40557. function _ChannelOrUri($name)
  40558. {
  40559. $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
  40560. 'Bundled package "%name%" can have either a uri or a channel, not both');
  40561. }
  40562. function _noChildTag($child, $tag)
  40563. {
  40564. $this->_stack->push(__FUNCTION__, 'error', array('child' => $child, 'tag' => $tag),
  40565. 'Tag <%tag%> is missing child tag <%child%>');
  40566. }
  40567. function _invalidVersion($type, $value)
  40568. {
  40569. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value),
  40570. 'Version type <%type%> is not a valid version (%value%)');
  40571. }
  40572. function _invalidState($type, $value)
  40573. {
  40574. $states = array('stable', 'beta', 'alpha', 'devel');
  40575. if ($type != 'api') {
  40576. $states[] = 'snapshot';
  40577. }
  40578. if (strtolower($value) == 'rc') {
  40579. $this->_stack->push(__FUNCTION__, 'error',
  40580. array('version' => $this->_packageInfo['version']['release']),
  40581. 'RC is not a state, it is a version postfix, try %version%RC1, stability beta');
  40582. }
  40583. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value,
  40584. 'types' => $states),
  40585. 'Stability type <%type%> is not a valid stability (%value%), must be one of ' .
  40586. '%types%');
  40587. }
  40588. function _invalidTask($task, $ret, $file)
  40589. {
  40590. switch ($ret[0]) {
  40591. case PEAR_TASK_ERROR_MISSING_ATTRIB :
  40592. $info = array('attrib' => $ret[1], 'task' => $task, 'file' => $file);
  40593. $msg = 'task <%task%> is missing attribute "%attrib%" in file %file%';
  40594. break;
  40595. case PEAR_TASK_ERROR_NOATTRIBS :
  40596. $info = array('task' => $task, 'file' => $file);
  40597. $msg = 'task <%task%> has no attributes in file %file%';
  40598. break;
  40599. case PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE :
  40600. $info = array('attrib' => $ret[1], 'values' => $ret[3],
  40601. 'was' => $ret[2], 'task' => $task, 'file' => $file);
  40602. $msg = 'task <%task%> attribute "%attrib%" has the wrong value "%was%" '.
  40603. 'in file %file%, expecting one of "%values%"';
  40604. break;
  40605. case PEAR_TASK_ERROR_INVALID :
  40606. $info = array('reason' => $ret[1], 'task' => $task, 'file' => $file);
  40607. $msg = 'task <%task%> in file %file% is invalid because of "%reason%"';
  40608. break;
  40609. }
  40610. $this->_stack->push(__FUNCTION__, 'error', $info, $msg);
  40611. }
  40612. function _unknownTask($task, $file)
  40613. {
  40614. $this->_stack->push(__FUNCTION__, 'error', array('task' => $task, 'file' => $file),
  40615. 'Unknown task "%task%" passed in file <file name="%file%">');
  40616. }
  40617. function _subpackageCannotProvideExtension($name)
  40618. {
  40619. $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
  40620. 'Subpackage dependency "%name%" cannot use <providesextension>, ' .
  40621. 'only package dependencies can use this tag');
  40622. }
  40623. function _subpackagesCannotConflict($name)
  40624. {
  40625. $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
  40626. 'Subpackage dependency "%name%" cannot use <conflicts/>, ' .
  40627. 'only package dependencies can use this tag');
  40628. }
  40629. function _cannotProvideExtension($release)
  40630. {
  40631. $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
  40632. '<%release%> packages cannot use <providesextension>, only extbinrelease, extsrcrelease, zendextsrcrelease, and zendextbinrelease can provide a PHP extension');
  40633. }
  40634. function _mustProvideExtension($release)
  40635. {
  40636. $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
  40637. '<%release%> packages must use <providesextension> to indicate which PHP extension is provided');
  40638. }
  40639. function _cannotHaveSrcpackage($release)
  40640. {
  40641. $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
  40642. '<%release%> packages cannot specify a source code package, only extension binaries may use the <srcpackage> tag');
  40643. }
  40644. function _mustSrcPackage($release)
  40645. {
  40646. $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
  40647. '<extbinrelease>/<zendextbinrelease> packages must specify a source code package with <srcpackage>');
  40648. }
  40649. function _mustSrcuri($release)
  40650. {
  40651. $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
  40652. '<extbinrelease>/<zendextbinrelease> packages must specify a source code package with <srcuri>');
  40653. }
  40654. function _uriDepsCannotHaveVersioning($type)
  40655. {
  40656. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
  40657. '%type%: dependencies with a <uri> tag cannot have any versioning information');
  40658. }
  40659. function _conflictingDepsCannotHaveVersioning($type)
  40660. {
  40661. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
  40662. '%type%: conflicting dependencies cannot have versioning info, use <exclude> to ' .
  40663. 'exclude specific versions of a dependency');
  40664. }
  40665. function _DepchannelCannotBeUri($type)
  40666. {
  40667. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
  40668. '%type%: channel cannot be __uri, this is a pseudo-channel reserved for uri ' .
  40669. 'dependencies only');
  40670. }
  40671. function _bundledPackagesMustBeFilename()
  40672. {
  40673. $this->_stack->push(__FUNCTION__, 'error', array(),
  40674. '<bundledpackage> tags must contain only the filename of a package release ' .
  40675. 'in the bundle');
  40676. }
  40677. function _binaryPackageMustBePackagename()
  40678. {
  40679. $this->_stack->push(__FUNCTION__, 'error', array(),
  40680. '<binarypackage> tags must contain the name of a package that is ' .
  40681. 'a compiled version of this extsrc/zendextsrc package');
  40682. }
  40683. function _fileNotFound($file)
  40684. {
  40685. $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
  40686. 'File "%file%" in package.xml does not exist');
  40687. }
  40688. function _notInContents($file, $tag)
  40689. {
  40690. $this->_stack->push(__FUNCTION__, 'error', array('file' => $file, 'tag' => $tag),
  40691. '<%tag% name="%file%"> is invalid, file is not in <contents>');
  40692. }
  40693. function _cannotValidateNoPathSet()
  40694. {
  40695. $this->_stack->push(__FUNCTION__, 'error', array(),
  40696. 'Cannot validate files, no path to package file is set (use setPackageFile())');
  40697. }
  40698. function _usesroletaskMustHaveChannelOrUri($role, $tag)
  40699. {
  40700. $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag),
  40701. '<%tag%> for role "%role%" must contain either <uri>, or <channel> and <package>');
  40702. }
  40703. function _usesroletaskMustHavePackage($role, $tag)
  40704. {
  40705. $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag),
  40706. '<%tag%> for role "%role%" must contain <package>');
  40707. }
  40708. function _usesroletaskMustHaveRoleTask($tag, $type)
  40709. {
  40710. $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag, 'type' => $type),
  40711. '<%tag%> must contain <%type%> defining the %type% to be used');
  40712. }
  40713. function _cannotConflictWithAllOs($type)
  40714. {
  40715. $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag),
  40716. '%tag% cannot conflict with all OSes');
  40717. }
  40718. function _invalidDepGroupName($name)
  40719. {
  40720. $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
  40721. 'Invalid dependency group name "%name%"');
  40722. }
  40723. function _multipleToplevelDirNotAllowed()
  40724. {
  40725. $this->_stack->push(__FUNCTION__, 'error', array(),
  40726. 'Multiple top-level <dir> tags are not allowed. Enclose them ' .
  40727. 'in a <dir name="/">');
  40728. }
  40729. function _multipleInstallAs($file)
  40730. {
  40731. $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
  40732. 'Only one <install> tag is allowed for file "%file%"');
  40733. }
  40734. function _ignoreAndInstallAs($file)
  40735. {
  40736. $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
  40737. 'Cannot have both <ignore> and <install> tags for file "%file%"');
  40738. }
  40739. function _analyzeBundledPackages()
  40740. {
  40741. if (!$this->_isValid) {
  40742. return false;
  40743. }
  40744. if (!$this->_pf->getPackageType() == 'bundle') {
  40745. return false;
  40746. }
  40747. if (!isset($this->_pf->_packageFile)) {
  40748. return false;
  40749. }
  40750. $dir_prefix = dirname($this->_pf->_packageFile);
  40751. $common = new PEAR_Common;
  40752. $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') :
  40753. array($common, 'log');
  40754. $info = $this->_pf->getContents();
  40755. $info = $info['bundledpackage'];
  40756. if (!is_array($info)) {
  40757. $info = array($info);
  40758. }
  40759. $pkg = new PEAR_PackageFile($this->_pf->_config);
  40760. foreach ($info as $package) {
  40761. if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $package)) {
  40762. $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $package);
  40763. $this->_isValid = 0;
  40764. continue;
  40765. }
  40766. call_user_func_array($log, array(1, "Analyzing bundled package $package"));
  40767. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  40768. $ret = $pkg->fromAnyFile($dir_prefix . DIRECTORY_SEPARATOR . $package,
  40769. PEAR_VALIDATE_NORMAL);
  40770. PEAR::popErrorHandling();
  40771. if (PEAR::isError($ret)) {
  40772. call_user_func_array($log, array(0, "ERROR: package $package is not a valid " .
  40773. 'package'));
  40774. $inf = $ret->getUserInfo();
  40775. if (is_array($inf)) {
  40776. foreach ($inf as $err) {
  40777. call_user_func_array($log, array(1, $err['message']));
  40778. }
  40779. }
  40780. return false;
  40781. }
  40782. }
  40783. return true;
  40784. }
  40785. function _analyzePhpFiles()
  40786. {
  40787. if (!$this->_isValid) {
  40788. return false;
  40789. }
  40790. if (!isset($this->_pf->_packageFile)) {
  40791. $this->_cannotValidateNoPathSet();
  40792. return false;
  40793. }
  40794. $dir_prefix = dirname($this->_pf->_packageFile);
  40795. $common = new PEAR_Common;
  40796. $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') :
  40797. array(&$common, 'log');
  40798. $info = $this->_pf->getContents();
  40799. if (!$info || !isset($info['dir']['file'])) {
  40800. $this->_tagCannotBeEmpty('contents><dir');
  40801. return false;
  40802. }
  40803. $info = $info['dir']['file'];
  40804. if (isset($info['attribs'])) {
  40805. $info = array($info);
  40806. }
  40807. $provides = array();
  40808. foreach ($info as $fa) {
  40809. $fa = $fa['attribs'];
  40810. $file = $fa['name'];
  40811. if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
  40812. $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $file);
  40813. $this->_isValid = 0;
  40814. continue;
  40815. }
  40816. if (in_array($fa['role'], PEAR_Installer_Role::getPhpRoles()) && $dir_prefix) {
  40817. call_user_func_array($log, array(1, "Analyzing $file"));
  40818. $srcinfo = $this->analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
  40819. if ($srcinfo) {
  40820. $provides = array_merge($provides, $this->_buildProvidesArray($srcinfo));
  40821. }
  40822. }
  40823. }
  40824. $this->_packageName = $pn = $this->_pf->getPackage();
  40825. $pnl = strlen($pn);
  40826. foreach ($provides as $key => $what) {
  40827. if (isset($what['explicit']) || !$what) {
  40828. // skip conformance checks if the provides entry is
  40829. // specified in the package.xml file
  40830. continue;
  40831. }
  40832. extract($what);
  40833. if ($type == 'class') {
  40834. if (!strncasecmp($name, $pn, $pnl)) {
  40835. continue;
  40836. }
  40837. $this->_stack->push(__FUNCTION__, 'warning',
  40838. array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn),
  40839. 'in %file%: %type% "%name%" not prefixed with package name "%package%"');
  40840. } elseif ($type == 'function') {
  40841. if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
  40842. continue;
  40843. }
  40844. $this->_stack->push(__FUNCTION__, 'warning',
  40845. array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn),
  40846. 'in %file%: %type% "%name%" not prefixed with package name "%package%"');
  40847. }
  40848. }
  40849. return $this->_isValid;
  40850. }
  40851. /**
  40852. * Analyze the source code of the given PHP file
  40853. *
  40854. * @param string Filename of the PHP file
  40855. * @param boolean whether to analyze $file as the file contents
  40856. * @return mixed
  40857. */
  40858. function analyzeSourceCode($file, $string = false)
  40859. {
  40860. if (!function_exists("token_get_all")) {
  40861. $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
  40862. 'Parser error: token_get_all() function must exist to analyze source code, PHP may have been compiled with --disable-tokenizer');
  40863. return false;
  40864. }
  40865. if (!defined('T_DOC_COMMENT')) {
  40866. define('T_DOC_COMMENT', T_COMMENT);
  40867. }
  40868. if (!defined('T_INTERFACE')) {
  40869. define('T_INTERFACE', -1);
  40870. }
  40871. if (!defined('T_IMPLEMENTS')) {
  40872. define('T_IMPLEMENTS', -1);
  40873. }
  40874. if ($string) {
  40875. $contents = $file;
  40876. } else {
  40877. if (!$fp = @fopen($file, "r")) {
  40878. return false;
  40879. }
  40880. fclose($fp);
  40881. $contents = file_get_contents($file);
  40882. }
  40883. // Silence this function so we can catch PHP Warnings and show our own custom message
  40884. $tokens = @token_get_all($contents);
  40885. if (isset($php_errormsg)) {
  40886. if (isset($this->_stack)) {
  40887. $pn = $this->_pf->getPackage();
  40888. $this->_stack->push(__FUNCTION__, 'warning',
  40889. array('file' => $file, 'package' => $pn),
  40890. 'in %file%: Could not process file for unknown reasons,' .
  40891. ' possibly a PHP parse error in %file% from %package%');
  40892. }
  40893. }
  40894. /*
  40895. for ($i = 0; $i < sizeof($tokens); $i++) {
  40896. @list($token, $data) = $tokens[$i];
  40897. if (is_string($token)) {
  40898. var_dump($token);
  40899. } else {
  40900. print token_name($token) . ' ';
  40901. var_dump(rtrim($data));
  40902. }
  40903. }
  40904. */
  40905. $look_for = 0;
  40906. $paren_level = 0;
  40907. $bracket_level = 0;
  40908. $brace_level = 0;
  40909. $lastphpdoc = '';
  40910. $current_class = '';
  40911. $current_interface = '';
  40912. $current_class_level = -1;
  40913. $current_function = '';
  40914. $current_function_level = -1;
  40915. $declared_classes = array();
  40916. $declared_interfaces = array();
  40917. $declared_functions = array();
  40918. $declared_methods = array();
  40919. $used_classes = array();
  40920. $used_functions = array();
  40921. $extends = array();
  40922. $implements = array();
  40923. $nodeps = array();
  40924. $inquote = false;
  40925. $interface = false;
  40926. for ($i = 0; $i < sizeof($tokens); $i++) {
  40927. if (is_array($tokens[$i])) {
  40928. list($token, $data) = $tokens[$i];
  40929. } else {
  40930. $token = $tokens[$i];
  40931. $data = '';
  40932. }
  40933. if ($inquote) {
  40934. if ($token != '"' && $token != T_END_HEREDOC) {
  40935. continue;
  40936. } else {
  40937. $inquote = false;
  40938. continue;
  40939. }
  40940. }
  40941. switch ($token) {
  40942. case T_WHITESPACE :
  40943. continue 2;
  40944. case ';':
  40945. if ($interface) {
  40946. $current_function = '';
  40947. $current_function_level = -1;
  40948. }
  40949. break;
  40950. case '"':
  40951. case T_START_HEREDOC:
  40952. $inquote = true;
  40953. break;
  40954. case T_CURLY_OPEN:
  40955. case T_DOLLAR_OPEN_CURLY_BRACES:
  40956. case '{': $brace_level++; continue 2;
  40957. case '}':
  40958. $brace_level--;
  40959. if ($current_class_level == $brace_level) {
  40960. $current_class = '';
  40961. $current_class_level = -1;
  40962. }
  40963. if ($current_function_level == $brace_level) {
  40964. $current_function = '';
  40965. $current_function_level = -1;
  40966. }
  40967. continue 2;
  40968. case '[': $bracket_level++; continue 2;
  40969. case ']': $bracket_level--; continue 2;
  40970. case '(': $paren_level++; continue 2;
  40971. case ')': $paren_level--; continue 2;
  40972. case T_INTERFACE:
  40973. $interface = true;
  40974. case T_CLASS:
  40975. if (($current_class_level != -1) || ($current_function_level != -1)) {
  40976. if (isset($this->_stack)) {
  40977. $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
  40978. 'Parser error: invalid PHP found in file "%file%"');
  40979. } else {
  40980. PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"",
  40981. PEAR_COMMON_ERROR_INVALIDPHP);
  40982. }
  40983. return false;
  40984. }
  40985. case T_FUNCTION:
  40986. case T_NEW:
  40987. case T_EXTENDS:
  40988. case T_IMPLEMENTS:
  40989. $look_for = $token;
  40990. continue 2;
  40991. case T_STRING:
  40992. if ($look_for == T_CLASS) {
  40993. $current_class = $data;
  40994. $current_class_level = $brace_level;
  40995. $declared_classes[] = $current_class;
  40996. } elseif ($look_for == T_INTERFACE) {
  40997. $current_interface = $data;
  40998. $current_class_level = $brace_level;
  40999. $declared_interfaces[] = $current_interface;
  41000. } elseif ($look_for == T_IMPLEMENTS) {
  41001. $implements[$current_class] = $data;
  41002. } elseif ($look_for == T_EXTENDS) {
  41003. $extends[$current_class] = $data;
  41004. } elseif ($look_for == T_FUNCTION) {
  41005. if ($current_class) {
  41006. $current_function = "$current_class::$data";
  41007. $declared_methods[$current_class][] = $data;
  41008. } elseif ($current_interface) {
  41009. $current_function = "$current_interface::$data";
  41010. $declared_methods[$current_interface][] = $data;
  41011. } else {
  41012. $current_function = $data;
  41013. $declared_functions[] = $current_function;
  41014. }
  41015. $current_function_level = $brace_level;
  41016. $m = array();
  41017. } elseif ($look_for == T_NEW) {
  41018. $used_classes[$data] = true;
  41019. }
  41020. $look_for = 0;
  41021. continue 2;
  41022. case T_VARIABLE:
  41023. $look_for = 0;
  41024. continue 2;
  41025. case T_DOC_COMMENT:
  41026. case T_COMMENT:
  41027. if (preg_match('!^/\*\*\s!', $data)) {
  41028. $lastphpdoc = $data;
  41029. if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
  41030. $nodeps = array_merge($nodeps, $m[1]);
  41031. }
  41032. }
  41033. continue 2;
  41034. case T_DOUBLE_COLON:
  41035. $token = $tokens[$i - 1][0];
  41036. if (!($token == T_WHITESPACE || $token == T_STRING || $token == T_STATIC || $token == T_VARIABLE)) {
  41037. if (isset($this->_stack)) {
  41038. $this->_stack->push(__FUNCTION__, 'warning', array('file' => $file),
  41039. 'Parser error: invalid PHP found in file "%file%"');
  41040. } else {
  41041. PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"",
  41042. PEAR_COMMON_ERROR_INVALIDPHP);
  41043. }
  41044. return false;
  41045. }
  41046. $class = $tokens[$i - 1][1];
  41047. if (strtolower($class) != 'parent') {
  41048. $used_classes[$class] = true;
  41049. }
  41050. continue 2;
  41051. }
  41052. }
  41053. return array(
  41054. "source_file" => $file,
  41055. "declared_classes" => $declared_classes,
  41056. "declared_interfaces" => $declared_interfaces,
  41057. "declared_methods" => $declared_methods,
  41058. "declared_functions" => $declared_functions,
  41059. "used_classes" => array_diff(array_keys($used_classes), $nodeps),
  41060. "inheritance" => $extends,
  41061. "implements" => $implements,
  41062. );
  41063. }
  41064. /**
  41065. * Build a "provides" array from data returned by
  41066. * analyzeSourceCode(). The format of the built array is like
  41067. * this:
  41068. *
  41069. * array(
  41070. * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
  41071. * ...
  41072. * )
  41073. *
  41074. *
  41075. * @param array $srcinfo array with information about a source file
  41076. * as returned by the analyzeSourceCode() method.
  41077. *
  41078. * @return void
  41079. *
  41080. * @access private
  41081. *
  41082. */
  41083. function _buildProvidesArray($srcinfo)
  41084. {
  41085. if (!$this->_isValid) {
  41086. return array();
  41087. }
  41088. $providesret = array();
  41089. $file = basename($srcinfo['source_file']);
  41090. $pn = isset($this->_pf) ? $this->_pf->getPackage() : '';
  41091. $pnl = strlen($pn);
  41092. foreach ($srcinfo['declared_classes'] as $class) {
  41093. $key = "class;$class";
  41094. if (isset($providesret[$key])) {
  41095. continue;
  41096. }
  41097. $providesret[$key] =
  41098. array('file'=> $file, 'type' => 'class', 'name' => $class);
  41099. if (isset($srcinfo['inheritance'][$class])) {
  41100. $providesret[$key]['extends'] =
  41101. $srcinfo['inheritance'][$class];
  41102. }
  41103. }
  41104. foreach ($srcinfo['declared_methods'] as $class => $methods) {
  41105. foreach ($methods as $method) {
  41106. $function = "$class::$method";
  41107. $key = "function;$function";
  41108. if ($method[0] == '_' || !strcasecmp($method, $class) ||
  41109. isset($providesret[$key])) {
  41110. continue;
  41111. }
  41112. $providesret[$key] =
  41113. array('file'=> $file, 'type' => 'function', 'name' => $function);
  41114. }
  41115. }
  41116. foreach ($srcinfo['declared_functions'] as $function) {
  41117. $key = "function;$function";
  41118. if ($function[0] == '_' || isset($providesret[$key])) {
  41119. continue;
  41120. }
  41121. if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
  41122. $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
  41123. }
  41124. $providesret[$key] =
  41125. array('file'=> $file, 'type' => 'function', 'name' => $function);
  41126. }
  41127. return $providesret;
  41128. }
  41129. }
  41130. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/PackageFile/v1.php����������������������������������������������������������������0000664�0001750�0001750�00000142663�14720722517�016225� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  41131. /**
  41132. * PEAR_PackageFile_v1, package.xml version 1.0
  41133. *
  41134. * PHP versions 4 and 5
  41135. *
  41136. * @category pear
  41137. * @package PEAR
  41138. * @author Greg Beaver <cellog@php.net>
  41139. * @copyright 1997-2009 The Authors
  41140. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  41141. * @link http://pear.php.net/package/PEAR
  41142. * @since File available since Release 1.4.0a1
  41143. */
  41144. /**
  41145. * For error handling
  41146. */
  41147. require_once 'PEAR/ErrorStack.php';
  41148. /**
  41149. * Error code if parsing is attempted with no xml extension
  41150. */
  41151. define('PEAR_PACKAGEFILE_ERROR_NO_XML_EXT', 3);
  41152. /**
  41153. * Error code if creating the xml parser resource fails
  41154. */
  41155. define('PEAR_PACKAGEFILE_ERROR_CANT_MAKE_PARSER', 4);
  41156. /**
  41157. * Error code used for all sax xml parsing errors
  41158. */
  41159. define('PEAR_PACKAGEFILE_ERROR_PARSER_ERROR', 5);
  41160. /**
  41161. * Error code used when there is no name
  41162. */
  41163. define('PEAR_PACKAGEFILE_ERROR_NO_NAME', 6);
  41164. /**
  41165. * Error code when a package name is not valid
  41166. */
  41167. define('PEAR_PACKAGEFILE_ERROR_INVALID_NAME', 7);
  41168. /**
  41169. * Error code used when no summary is parsed
  41170. */
  41171. define('PEAR_PACKAGEFILE_ERROR_NO_SUMMARY', 8);
  41172. /**
  41173. * Error code for summaries that are more than 1 line
  41174. */
  41175. define('PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY', 9);
  41176. /**
  41177. * Error code used when no description is present
  41178. */
  41179. define('PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION', 10);
  41180. /**
  41181. * Error code used when no license is present
  41182. */
  41183. define('PEAR_PACKAGEFILE_ERROR_NO_LICENSE', 11);
  41184. /**
  41185. * Error code used when a <version> version number is not present
  41186. */
  41187. define('PEAR_PACKAGEFILE_ERROR_NO_VERSION', 12);
  41188. /**
  41189. * Error code used when a <version> version number is invalid
  41190. */
  41191. define('PEAR_PACKAGEFILE_ERROR_INVALID_VERSION', 13);
  41192. /**
  41193. * Error code when release state is missing
  41194. */
  41195. define('PEAR_PACKAGEFILE_ERROR_NO_STATE', 14);
  41196. /**
  41197. * Error code when release state is invalid
  41198. */
  41199. define('PEAR_PACKAGEFILE_ERROR_INVALID_STATE', 15);
  41200. /**
  41201. * Error code when release state is missing
  41202. */
  41203. define('PEAR_PACKAGEFILE_ERROR_NO_DATE', 16);
  41204. /**
  41205. * Error code when release state is invalid
  41206. */
  41207. define('PEAR_PACKAGEFILE_ERROR_INVALID_DATE', 17);
  41208. /**
  41209. * Error code when no release notes are found
  41210. */
  41211. define('PEAR_PACKAGEFILE_ERROR_NO_NOTES', 18);
  41212. /**
  41213. * Error code when no maintainers are found
  41214. */
  41215. define('PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS', 19);
  41216. /**
  41217. * Error code when a maintainer has no handle
  41218. */
  41219. define('PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE', 20);
  41220. /**
  41221. * Error code when a maintainer has no handle
  41222. */
  41223. define('PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE', 21);
  41224. /**
  41225. * Error code when a maintainer has no name
  41226. */
  41227. define('PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME', 22);
  41228. /**
  41229. * Error code when a maintainer has no email
  41230. */
  41231. define('PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL', 23);
  41232. /**
  41233. * Error code when a maintainer has no handle
  41234. */
  41235. define('PEAR_PACKAGEFILE_ERROR_INVALID_MAINTROLE', 24);
  41236. /**
  41237. * Error code when a dependency is not a PHP dependency, but has no name
  41238. */
  41239. define('PEAR_PACKAGEFILE_ERROR_NO_DEPNAME', 25);
  41240. /**
  41241. * Error code when a dependency has no type (pkg, php, etc.)
  41242. */
  41243. define('PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE', 26);
  41244. /**
  41245. * Error code when a dependency has no relation (lt, ge, has, etc.)
  41246. */
  41247. define('PEAR_PACKAGEFILE_ERROR_NO_DEPREL', 27);
  41248. /**
  41249. * Error code when a dependency is not a 'has' relation, but has no version
  41250. */
  41251. define('PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION', 28);
  41252. /**
  41253. * Error code when a dependency has an invalid relation
  41254. */
  41255. define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPREL', 29);
  41256. /**
  41257. * Error code when a dependency has an invalid type
  41258. */
  41259. define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPTYPE', 30);
  41260. /**
  41261. * Error code when a dependency has an invalid optional option
  41262. */
  41263. define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL', 31);
  41264. /**
  41265. * Error code when a dependency is a pkg dependency, and has an invalid package name
  41266. */
  41267. define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPNAME', 32);
  41268. /**
  41269. * Error code when a dependency has a channel="foo" attribute, and foo is not a registered channel
  41270. */
  41271. define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_DEPCHANNEL', 33);
  41272. /**
  41273. * Error code when rel="has" and version attribute is present.
  41274. */
  41275. define('PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED', 34);
  41276. /**
  41277. * Error code when type="php" and dependency name is present
  41278. */
  41279. define('PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED', 35);
  41280. /**
  41281. * Error code when a configure option has no name
  41282. */
  41283. define('PEAR_PACKAGEFILE_ERROR_NO_CONFNAME', 36);
  41284. /**
  41285. * Error code when a configure option has no name
  41286. */
  41287. define('PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT', 37);
  41288. /**
  41289. * Error code when a file in the filelist has an invalid role
  41290. */
  41291. define('PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE', 38);
  41292. /**
  41293. * Error code when a file in the filelist has no role
  41294. */
  41295. define('PEAR_PACKAGEFILE_ERROR_NO_FILEROLE', 39);
  41296. /**
  41297. * Error code when analyzing a php source file that has parse errors
  41298. */
  41299. define('PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE', 40);
  41300. /**
  41301. * Error code when analyzing a php source file reveals a source element
  41302. * without a package name prefix
  41303. */
  41304. define('PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX', 41);
  41305. /**
  41306. * Error code when an unknown channel is specified
  41307. */
  41308. define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_CHANNEL', 42);
  41309. /**
  41310. * Error code when no files are found in the filelist
  41311. */
  41312. define('PEAR_PACKAGEFILE_ERROR_NO_FILES', 43);
  41313. /**
  41314. * Error code when a file is not valid php according to _analyzeSourceCode()
  41315. */
  41316. define('PEAR_PACKAGEFILE_ERROR_INVALID_FILE', 44);
  41317. /**
  41318. * Error code when the channel validator returns an error or warning
  41319. */
  41320. define('PEAR_PACKAGEFILE_ERROR_CHANNELVAL', 45);
  41321. /**
  41322. * Error code when a php5 package is packaged in php4 (analysis doesn't work)
  41323. */
  41324. define('PEAR_PACKAGEFILE_ERROR_PHP5', 46);
  41325. /**
  41326. * Error code when a file is listed in package.xml but does not exist
  41327. */
  41328. define('PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND', 47);
  41329. /**
  41330. * Error code when a <dep type="php" rel="not"... is encountered (use rel="ne")
  41331. */
  41332. define('PEAR_PACKAGEFILE_PHP_NO_NOT', 48);
  41333. /**
  41334. * Error code when a package.xml contains non-ISO-8859-1 characters
  41335. */
  41336. define('PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS', 49);
  41337. /**
  41338. * Error code when a dependency is not a 'has' relation, but has no version
  41339. */
  41340. define('PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION', 50);
  41341. /**
  41342. * Error code when a package has no lead developer
  41343. */
  41344. define('PEAR_PACKAGEFILE_ERROR_NO_LEAD', 51);
  41345. /**
  41346. * Error code when a filename begins with "."
  41347. */
  41348. define('PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME', 52);
  41349. /**
  41350. * package.xml encapsulator
  41351. * @category pear
  41352. * @package PEAR
  41353. * @author Greg Beaver <cellog@php.net>
  41354. * @copyright 1997-2009 The Authors
  41355. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  41356. * @version Release: 1.10.16
  41357. * @link http://pear.php.net/package/PEAR
  41358. * @since Class available since Release 1.4.0a1
  41359. */
  41360. class PEAR_PackageFile_v1
  41361. {
  41362. /**
  41363. * @access private
  41364. * @var PEAR_ErrorStack
  41365. * @access private
  41366. */
  41367. var $_stack;
  41368. /**
  41369. * A registry object, used to access the package name validation regex for non-standard channels
  41370. * @var PEAR_Registry
  41371. * @access private
  41372. */
  41373. var $_registry;
  41374. /**
  41375. * An object that contains a log method that matches PEAR_Common::log's signature
  41376. * @var object
  41377. * @access private
  41378. */
  41379. var $_logger;
  41380. /**
  41381. * Parsed package information
  41382. * @var array
  41383. * @access private
  41384. */
  41385. var $_packageInfo;
  41386. /**
  41387. * path to package.xml
  41388. * @var string
  41389. * @access private
  41390. */
  41391. var $_packageFile;
  41392. /**
  41393. * path to package .tgz or false if this is a local/extracted package.xml
  41394. * @var string
  41395. * @access private
  41396. */
  41397. var $_archiveFile;
  41398. /**
  41399. * @var int
  41400. * @access private
  41401. */
  41402. var $_isValid = 0;
  41403. /**
  41404. * Determines whether this packagefile was initialized only with partial package info
  41405. *
  41406. * If this package file was constructed via parsing REST, it will only contain
  41407. *
  41408. * - package name
  41409. * - channel name
  41410. * - dependencies
  41411. * @var boolean
  41412. * @access private
  41413. */
  41414. var $_incomplete = true;
  41415. /**
  41416. * @param bool determines whether to return a PEAR_Error object, or use the PEAR_ErrorStack
  41417. * @param string Name of Error Stack class to use.
  41418. */
  41419. function __construct()
  41420. {
  41421. $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v1');
  41422. $this->_stack->setErrorMessageTemplate($this->_getErrorMessage());
  41423. $this->_isValid = 0;
  41424. }
  41425. function installBinary($installer)
  41426. {
  41427. return false;
  41428. }
  41429. function isExtension($name)
  41430. {
  41431. return false;
  41432. }
  41433. function setConfig(&$config)
  41434. {
  41435. $this->_config = &$config;
  41436. $this->_registry = &$config->getRegistry();
  41437. }
  41438. function setRequestedGroup()
  41439. {
  41440. // placeholder
  41441. }
  41442. /**
  41443. * For saving in the registry.
  41444. *
  41445. * Set the last version that was installed
  41446. * @param string
  41447. */
  41448. function setLastInstalledVersion($version)
  41449. {
  41450. $this->_packageInfo['_lastversion'] = $version;
  41451. }
  41452. /**
  41453. * @return string|false
  41454. */
  41455. function getLastInstalledVersion()
  41456. {
  41457. if (isset($this->_packageInfo['_lastversion'])) {
  41458. return $this->_packageInfo['_lastversion'];
  41459. }
  41460. return false;
  41461. }
  41462. function getInstalledBinary()
  41463. {
  41464. return false;
  41465. }
  41466. function listPostinstallScripts()
  41467. {
  41468. return false;
  41469. }
  41470. function initPostinstallScripts()
  41471. {
  41472. return false;
  41473. }
  41474. function setLogger(&$logger)
  41475. {
  41476. if ($logger && (!is_object($logger) || !method_exists($logger, 'log'))) {
  41477. return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
  41478. }
  41479. $this->_logger = &$logger;
  41480. }
  41481. function setPackagefile($file, $archive = false)
  41482. {
  41483. $this->_packageFile = $file;
  41484. $this->_archiveFile = $archive ? $archive : $file;
  41485. }
  41486. function getPackageFile()
  41487. {
  41488. return isset($this->_packageFile) ? $this->_packageFile : false;
  41489. }
  41490. function getPackageType()
  41491. {
  41492. return 'php';
  41493. }
  41494. function getArchiveFile()
  41495. {
  41496. return $this->_archiveFile;
  41497. }
  41498. function packageInfo($field)
  41499. {
  41500. if (!is_string($field) || empty($field) ||
  41501. !isset($this->_packageInfo[$field])) {
  41502. return false;
  41503. }
  41504. return $this->_packageInfo[$field];
  41505. }
  41506. function setDirtree($path)
  41507. {
  41508. if (!isset($this->_packageInfo['dirtree'])) {
  41509. $this->_packageInfo['dirtree'] = array();
  41510. }
  41511. $this->_packageInfo['dirtree'][$path] = true;
  41512. }
  41513. function getDirtree()
  41514. {
  41515. if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
  41516. return $this->_packageInfo['dirtree'];
  41517. }
  41518. return false;
  41519. }
  41520. function resetDirtree()
  41521. {
  41522. unset($this->_packageInfo['dirtree']);
  41523. }
  41524. function fromArray($pinfo)
  41525. {
  41526. $this->_incomplete = false;
  41527. $this->_packageInfo = $pinfo;
  41528. }
  41529. function isIncomplete()
  41530. {
  41531. return $this->_incomplete;
  41532. }
  41533. function getChannel()
  41534. {
  41535. return 'pear.php.net';
  41536. }
  41537. function getUri()
  41538. {
  41539. return false;
  41540. }
  41541. function getTime()
  41542. {
  41543. return false;
  41544. }
  41545. function getExtends()
  41546. {
  41547. if (isset($this->_packageInfo['extends'])) {
  41548. return $this->_packageInfo['extends'];
  41549. }
  41550. return false;
  41551. }
  41552. /**
  41553. * @return array
  41554. */
  41555. function toArray()
  41556. {
  41557. if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
  41558. return false;
  41559. }
  41560. return $this->getArray();
  41561. }
  41562. function getArray()
  41563. {
  41564. return $this->_packageInfo;
  41565. }
  41566. function getName()
  41567. {
  41568. return $this->getPackage();
  41569. }
  41570. function getPackage()
  41571. {
  41572. if (isset($this->_packageInfo['package'])) {
  41573. return $this->_packageInfo['package'];
  41574. }
  41575. return false;
  41576. }
  41577. /**
  41578. * WARNING - don't use this unless you know what you are doing
  41579. */
  41580. function setRawPackage($package)
  41581. {
  41582. $this->_packageInfo['package'] = $package;
  41583. }
  41584. function setPackage($package)
  41585. {
  41586. $this->_packageInfo['package'] = $package;
  41587. $this->_isValid = false;
  41588. }
  41589. function getVersion()
  41590. {
  41591. if (isset($this->_packageInfo['version'])) {
  41592. return $this->_packageInfo['version'];
  41593. }
  41594. return false;
  41595. }
  41596. function setVersion($version)
  41597. {
  41598. $this->_packageInfo['version'] = $version;
  41599. $this->_isValid = false;
  41600. }
  41601. function clearMaintainers()
  41602. {
  41603. unset($this->_packageInfo['maintainers']);
  41604. }
  41605. function getMaintainers()
  41606. {
  41607. if (isset($this->_packageInfo['maintainers'])) {
  41608. return $this->_packageInfo['maintainers'];
  41609. }
  41610. return false;
  41611. }
  41612. /**
  41613. * Adds a new maintainer - no checking of duplicates is performed, use
  41614. * updatemaintainer for that purpose.
  41615. */
  41616. function addMaintainer($role, $handle, $name, $email)
  41617. {
  41618. $this->_packageInfo['maintainers'][] =
  41619. array('handle' => $handle, 'role' => $role, 'email' => $email, 'name' => $name);
  41620. $this->_isValid = false;
  41621. }
  41622. function updateMaintainer($role, $handle, $name, $email)
  41623. {
  41624. $found = false;
  41625. if (!isset($this->_packageInfo['maintainers']) ||
  41626. !is_array($this->_packageInfo['maintainers'])) {
  41627. return $this->addMaintainer($role, $handle, $name, $email);
  41628. }
  41629. foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) {
  41630. if ($maintainer['handle'] == $handle) {
  41631. $found = $i;
  41632. break;
  41633. }
  41634. }
  41635. if ($found !== false) {
  41636. unset($this->_packageInfo['maintainers'][$found]);
  41637. $this->_packageInfo['maintainers'] =
  41638. array_values($this->_packageInfo['maintainers']);
  41639. }
  41640. $this->addMaintainer($role, $handle, $name, $email);
  41641. }
  41642. function deleteMaintainer($handle)
  41643. {
  41644. $found = false;
  41645. foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) {
  41646. if ($maintainer['handle'] == $handle) {
  41647. $found = $i;
  41648. break;
  41649. }
  41650. }
  41651. if ($found !== false) {
  41652. unset($this->_packageInfo['maintainers'][$found]);
  41653. $this->_packageInfo['maintainers'] =
  41654. array_values($this->_packageInfo['maintainers']);
  41655. return true;
  41656. }
  41657. return false;
  41658. }
  41659. function getState()
  41660. {
  41661. if (isset($this->_packageInfo['release_state'])) {
  41662. return $this->_packageInfo['release_state'];
  41663. }
  41664. return false;
  41665. }
  41666. function setRawState($state)
  41667. {
  41668. $this->_packageInfo['release_state'] = $state;
  41669. }
  41670. function setState($state)
  41671. {
  41672. $this->_packageInfo['release_state'] = $state;
  41673. $this->_isValid = false;
  41674. }
  41675. function getDate()
  41676. {
  41677. if (isset($this->_packageInfo['release_date'])) {
  41678. return $this->_packageInfo['release_date'];
  41679. }
  41680. return false;
  41681. }
  41682. function setDate($date)
  41683. {
  41684. $this->_packageInfo['release_date'] = $date;
  41685. $this->_isValid = false;
  41686. }
  41687. function getLicense()
  41688. {
  41689. if (isset($this->_packageInfo['release_license'])) {
  41690. return $this->_packageInfo['release_license'];
  41691. }
  41692. return false;
  41693. }
  41694. function setLicense($date)
  41695. {
  41696. $this->_packageInfo['release_license'] = $date;
  41697. $this->_isValid = false;
  41698. }
  41699. function getSummary()
  41700. {
  41701. if (isset($this->_packageInfo['summary'])) {
  41702. return $this->_packageInfo['summary'];
  41703. }
  41704. return false;
  41705. }
  41706. function setSummary($summary)
  41707. {
  41708. $this->_packageInfo['summary'] = $summary;
  41709. $this->_isValid = false;
  41710. }
  41711. function getDescription()
  41712. {
  41713. if (isset($this->_packageInfo['description'])) {
  41714. return $this->_packageInfo['description'];
  41715. }
  41716. return false;
  41717. }
  41718. function setDescription($desc)
  41719. {
  41720. $this->_packageInfo['description'] = $desc;
  41721. $this->_isValid = false;
  41722. }
  41723. function getNotes()
  41724. {
  41725. if (isset($this->_packageInfo['release_notes'])) {
  41726. return $this->_packageInfo['release_notes'];
  41727. }
  41728. return false;
  41729. }
  41730. function setNotes($notes)
  41731. {
  41732. $this->_packageInfo['release_notes'] = $notes;
  41733. $this->_isValid = false;
  41734. }
  41735. function getDeps()
  41736. {
  41737. if (isset($this->_packageInfo['release_deps'])) {
  41738. return $this->_packageInfo['release_deps'];
  41739. }
  41740. return false;
  41741. }
  41742. /**
  41743. * Reset dependencies prior to adding new ones
  41744. */
  41745. function clearDeps()
  41746. {
  41747. unset($this->_packageInfo['release_deps']);
  41748. }
  41749. function addPhpDep($version, $rel)
  41750. {
  41751. $this->_isValid = false;
  41752. $this->_packageInfo['release_deps'][] =
  41753. array('type' => 'php',
  41754. 'rel' => $rel,
  41755. 'version' => $version);
  41756. }
  41757. function addPackageDep($name, $version, $rel, $optional = 'no')
  41758. {
  41759. $this->_isValid = false;
  41760. $dep =
  41761. array('type' => 'pkg',
  41762. 'name' => $name,
  41763. 'rel' => $rel,
  41764. 'optional' => $optional);
  41765. if ($rel != 'has' && $rel != 'not') {
  41766. $dep['version'] = $version;
  41767. }
  41768. $this->_packageInfo['release_deps'][] = $dep;
  41769. }
  41770. function addExtensionDep($name, $version, $rel, $optional = 'no')
  41771. {
  41772. $this->_isValid = false;
  41773. $this->_packageInfo['release_deps'][] =
  41774. array('type' => 'ext',
  41775. 'name' => $name,
  41776. 'rel' => $rel,
  41777. 'version' => $version,
  41778. 'optional' => $optional);
  41779. }
  41780. /**
  41781. * WARNING - do not use this function directly unless you know what you're doing
  41782. */
  41783. function setDeps($deps)
  41784. {
  41785. $this->_packageInfo['release_deps'] = $deps;
  41786. }
  41787. function hasDeps()
  41788. {
  41789. return isset($this->_packageInfo['release_deps']) &&
  41790. count($this->_packageInfo['release_deps']);
  41791. }
  41792. function getDependencyGroup($group)
  41793. {
  41794. return false;
  41795. }
  41796. function isCompatible($pf)
  41797. {
  41798. return false;
  41799. }
  41800. function isSubpackageOf($p)
  41801. {
  41802. return $p->isSubpackage($this);
  41803. }
  41804. function isSubpackage($p)
  41805. {
  41806. return false;
  41807. }
  41808. function dependsOn($package, $channel)
  41809. {
  41810. if (strtolower($channel) != 'pear.php.net') {
  41811. return false;
  41812. }
  41813. if (!($deps = $this->getDeps())) {
  41814. return false;
  41815. }
  41816. foreach ($deps as $dep) {
  41817. if ($dep['type'] != 'pkg') {
  41818. continue;
  41819. }
  41820. if (strtolower($dep['name']) == strtolower($package)) {
  41821. return true;
  41822. }
  41823. }
  41824. return false;
  41825. }
  41826. function getConfigureOptions()
  41827. {
  41828. if (isset($this->_packageInfo['configure_options'])) {
  41829. return $this->_packageInfo['configure_options'];
  41830. }
  41831. return false;
  41832. }
  41833. function hasConfigureOptions()
  41834. {
  41835. return isset($this->_packageInfo['configure_options']) &&
  41836. count($this->_packageInfo['configure_options']);
  41837. }
  41838. function addConfigureOption($name, $prompt, $default = false)
  41839. {
  41840. $o = array('name' => $name, 'prompt' => $prompt);
  41841. if ($default !== false) {
  41842. $o['default'] = $default;
  41843. }
  41844. if (!isset($this->_packageInfo['configure_options'])) {
  41845. $this->_packageInfo['configure_options'] = array();
  41846. }
  41847. $this->_packageInfo['configure_options'][] = $o;
  41848. }
  41849. function clearConfigureOptions()
  41850. {
  41851. unset($this->_packageInfo['configure_options']);
  41852. }
  41853. function getProvides()
  41854. {
  41855. if (isset($this->_packageInfo['provides'])) {
  41856. return $this->_packageInfo['provides'];
  41857. }
  41858. return false;
  41859. }
  41860. function getProvidesExtension()
  41861. {
  41862. return false;
  41863. }
  41864. function addFile($dir, $file, $attrs)
  41865. {
  41866. $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir);
  41867. if ($dir == '/' || $dir == '') {
  41868. $dir = '';
  41869. } else {
  41870. $dir .= '/';
  41871. }
  41872. $file = $dir . $file;
  41873. $file = preg_replace('![\\/]+!', '/', $file);
  41874. $this->_packageInfo['filelist'][$file] = $attrs;
  41875. }
  41876. function getInstallationFilelist()
  41877. {
  41878. return $this->getFilelist();
  41879. }
  41880. function getFilelist()
  41881. {
  41882. if (isset($this->_packageInfo['filelist'])) {
  41883. return $this->_packageInfo['filelist'];
  41884. }
  41885. return false;
  41886. }
  41887. function setFileAttribute($file, $attr, $value)
  41888. {
  41889. $this->_packageInfo['filelist'][$file][$attr] = $value;
  41890. }
  41891. function resetFilelist()
  41892. {
  41893. $this->_packageInfo['filelist'] = array();
  41894. }
  41895. function setInstalledAs($file, $path)
  41896. {
  41897. if ($path) {
  41898. return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
  41899. }
  41900. unset($this->_packageInfo['filelist'][$file]['installed_as']);
  41901. }
  41902. function installedFile($file, $atts)
  41903. {
  41904. if (isset($this->_packageInfo['filelist'][$file])) {
  41905. $this->_packageInfo['filelist'][$file] =
  41906. array_merge($this->_packageInfo['filelist'][$file], $atts);
  41907. } else {
  41908. $this->_packageInfo['filelist'][$file] = $atts;
  41909. }
  41910. }
  41911. function getChangelog()
  41912. {
  41913. if (isset($this->_packageInfo['changelog'])) {
  41914. return $this->_packageInfo['changelog'];
  41915. }
  41916. return false;
  41917. }
  41918. function getPackagexmlVersion()
  41919. {
  41920. return '1.0';
  41921. }
  41922. /**
  41923. * Wrapper to {@link PEAR_ErrorStack::getErrors()}
  41924. * @param boolean determines whether to purge the error stack after retrieving
  41925. * @return array
  41926. */
  41927. function getValidationWarnings($purge = true)
  41928. {
  41929. return $this->_stack->getErrors($purge);
  41930. }
  41931. // }}}
  41932. /**
  41933. * Validation error. Also marks the object contents as invalid
  41934. * @param error code
  41935. * @param array error information
  41936. * @access private
  41937. */
  41938. function _validateError($code, $params = array())
  41939. {
  41940. $this->_stack->push($code, 'error', $params, false, false, debug_backtrace());
  41941. $this->_isValid = false;
  41942. }
  41943. /**
  41944. * Validation warning. Does not mark the object contents invalid.
  41945. * @param error code
  41946. * @param array error information
  41947. * @access private
  41948. */
  41949. function _validateWarning($code, $params = array())
  41950. {
  41951. $this->_stack->push($code, 'warning', $params, false, false, debug_backtrace());
  41952. }
  41953. /**
  41954. * @param integer error code
  41955. * @access protected
  41956. */
  41957. function _getErrorMessage()
  41958. {
  41959. return array(
  41960. PEAR_PACKAGEFILE_ERROR_NO_NAME =>
  41961. 'Missing Package Name',
  41962. PEAR_PACKAGEFILE_ERROR_NO_SUMMARY =>
  41963. 'No summary found',
  41964. PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY =>
  41965. 'Summary should be on one line',
  41966. PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION =>
  41967. 'Missing description',
  41968. PEAR_PACKAGEFILE_ERROR_NO_LICENSE =>
  41969. 'Missing license',
  41970. PEAR_PACKAGEFILE_ERROR_NO_VERSION =>
  41971. 'No release version found',
  41972. PEAR_PACKAGEFILE_ERROR_NO_STATE =>
  41973. 'No release state found',
  41974. PEAR_PACKAGEFILE_ERROR_NO_DATE =>
  41975. 'No release date found',
  41976. PEAR_PACKAGEFILE_ERROR_NO_NOTES =>
  41977. 'No release notes found',
  41978. PEAR_PACKAGEFILE_ERROR_NO_LEAD =>
  41979. 'Package must have at least one lead maintainer',
  41980. PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS =>
  41981. 'No maintainers found, at least one must be defined',
  41982. PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE =>
  41983. 'Maintainer %index% has no handle (user ID at channel server)',
  41984. PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE =>
  41985. 'Maintainer %index% has no role',
  41986. PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME =>
  41987. 'Maintainer %index% has no name',
  41988. PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL =>
  41989. 'Maintainer %index% has no email',
  41990. PEAR_PACKAGEFILE_ERROR_NO_DEPNAME =>
  41991. 'Dependency %index% is not a php dependency, and has no name',
  41992. PEAR_PACKAGEFILE_ERROR_NO_DEPREL =>
  41993. 'Dependency %index% has no relation (rel)',
  41994. PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE =>
  41995. 'Dependency %index% has no type',
  41996. PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED =>
  41997. 'PHP Dependency %index% has a name attribute of "%name%" which will be' .
  41998. ' ignored!',
  41999. PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION =>
  42000. 'Dependency %index% is not a rel="has" or rel="not" dependency, ' .
  42001. 'and has no version',
  42002. PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION =>
  42003. 'Dependency %index% is a type="php" dependency, ' .
  42004. 'and has no version',
  42005. PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED =>
  42006. 'Dependency %index% is a rel="%rel%" dependency, versioning is ignored',
  42007. PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL =>
  42008. 'Dependency %index% has invalid optional value "%opt%", should be yes or no',
  42009. PEAR_PACKAGEFILE_PHP_NO_NOT =>
  42010. 'Dependency %index%: php dependencies cannot use "not" rel, use "ne"' .
  42011. ' to exclude specific versions',
  42012. PEAR_PACKAGEFILE_ERROR_NO_CONFNAME =>
  42013. 'Configure Option %index% has no name',
  42014. PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT =>
  42015. 'Configure Option %index% has no prompt',
  42016. PEAR_PACKAGEFILE_ERROR_NO_FILES =>
  42017. 'No files in <filelist> section of package.xml',
  42018. PEAR_PACKAGEFILE_ERROR_NO_FILEROLE =>
  42019. 'File "%file%" has no role, expecting one of "%roles%"',
  42020. PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE =>
  42021. 'File "%file%" has invalid role "%role%", expecting one of "%roles%"',
  42022. PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME =>
  42023. 'File "%file%" cannot start with ".", cannot package or install',
  42024. PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE =>
  42025. 'Parser error: invalid PHP found in file "%file%"',
  42026. PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX =>
  42027. 'in %file%: %type% "%name%" not prefixed with package name "%package%"',
  42028. PEAR_PACKAGEFILE_ERROR_INVALID_FILE =>
  42029. 'Parser error: invalid PHP file "%file%"',
  42030. PEAR_PACKAGEFILE_ERROR_CHANNELVAL =>
  42031. 'Channel validator error: field "%field%" - %reason%',
  42032. PEAR_PACKAGEFILE_ERROR_PHP5 =>
  42033. 'Error, PHP5 token encountered in %file%, analysis should be in PHP5',
  42034. PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND =>
  42035. 'File "%file%" in package.xml does not exist',
  42036. PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS =>
  42037. 'Package.xml contains non-ISO-8859-1 characters, and may not validate',
  42038. );
  42039. }
  42040. /**
  42041. * Validate XML package definition file.
  42042. *
  42043. * @access public
  42044. * @return boolean
  42045. */
  42046. function validate($state = PEAR_VALIDATE_NORMAL, $nofilechecking = false)
  42047. {
  42048. if (($this->_isValid & $state) == $state) {
  42049. return true;
  42050. }
  42051. $this->_isValid = true;
  42052. $info = $this->_packageInfo;
  42053. if (empty($info['package'])) {
  42054. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NAME);
  42055. $this->_packageName = $pn = 'unknown';
  42056. } else {
  42057. $this->_packageName = $pn = $info['package'];
  42058. }
  42059. if (empty($info['summary'])) {
  42060. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_SUMMARY);
  42061. } elseif (strpos(trim($info['summary']), "\n") !== false) {
  42062. $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY,
  42063. array('summary' => $info['summary']));
  42064. }
  42065. if (empty($info['description'])) {
  42066. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION);
  42067. }
  42068. if (empty($info['release_license'])) {
  42069. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LICENSE);
  42070. }
  42071. if (empty($info['version'])) {
  42072. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_VERSION);
  42073. }
  42074. if (empty($info['release_state'])) {
  42075. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_STATE);
  42076. }
  42077. if (empty($info['release_date'])) {
  42078. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DATE);
  42079. }
  42080. if (empty($info['release_notes'])) {
  42081. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NOTES);
  42082. }
  42083. if (empty($info['maintainers'])) {
  42084. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS);
  42085. } else {
  42086. $haslead = false;
  42087. $i = 1;
  42088. foreach ($info['maintainers'] as $m) {
  42089. if (empty($m['handle'])) {
  42090. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE,
  42091. array('index' => $i));
  42092. }
  42093. if (empty($m['role'])) {
  42094. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE,
  42095. array('index' => $i, 'roles' => PEAR_Common::getUserRoles()));
  42096. } elseif ($m['role'] == 'lead') {
  42097. $haslead = true;
  42098. }
  42099. if (empty($m['name'])) {
  42100. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME,
  42101. array('index' => $i));
  42102. }
  42103. if (empty($m['email'])) {
  42104. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL,
  42105. array('index' => $i));
  42106. }
  42107. $i++;
  42108. }
  42109. if (!$haslead) {
  42110. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LEAD);
  42111. }
  42112. }
  42113. if (!empty($info['release_deps'])) {
  42114. $i = 1;
  42115. foreach ($info['release_deps'] as $d) {
  42116. if (!isset($d['type']) || empty($d['type'])) {
  42117. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE,
  42118. array('index' => $i, 'types' => PEAR_Common::getDependencyTypes()));
  42119. continue;
  42120. }
  42121. if (!isset($d['rel']) || empty($d['rel'])) {
  42122. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPREL,
  42123. array('index' => $i, 'rels' => PEAR_Common::getDependencyRelations()));
  42124. continue;
  42125. }
  42126. if (!empty($d['optional'])) {
  42127. if (!in_array($d['optional'], array('yes', 'no'))) {
  42128. $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL,
  42129. array('index' => $i, 'opt' => $d['optional']));
  42130. }
  42131. }
  42132. if ($d['rel'] != 'has' && $d['rel'] != 'not' && empty($d['version'])) {
  42133. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION,
  42134. array('index' => $i));
  42135. } elseif (($d['rel'] == 'has' || $d['rel'] == 'not') && !empty($d['version'])) {
  42136. $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED,
  42137. array('index' => $i, 'rel' => $d['rel']));
  42138. }
  42139. if ($d['type'] == 'php' && !empty($d['name'])) {
  42140. $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED,
  42141. array('index' => $i, 'name' => $d['name']));
  42142. } elseif ($d['type'] != 'php' && empty($d['name'])) {
  42143. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPNAME,
  42144. array('index' => $i));
  42145. }
  42146. if ($d['type'] == 'php' && empty($d['version'])) {
  42147. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION,
  42148. array('index' => $i));
  42149. }
  42150. if (($d['rel'] == 'not') && ($d['type'] == 'php')) {
  42151. $this->_validateError(PEAR_PACKAGEFILE_PHP_NO_NOT,
  42152. array('index' => $i));
  42153. }
  42154. $i++;
  42155. }
  42156. }
  42157. if (!empty($info['configure_options'])) {
  42158. $i = 1;
  42159. foreach ($info['configure_options'] as $c) {
  42160. if (empty($c['name'])) {
  42161. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFNAME,
  42162. array('index' => $i));
  42163. }
  42164. if (empty($c['prompt'])) {
  42165. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT,
  42166. array('index' => $i));
  42167. }
  42168. $i++;
  42169. }
  42170. }
  42171. if (empty($info['filelist'])) {
  42172. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILES);
  42173. $errors[] = 'no files';
  42174. } else {
  42175. foreach ($info['filelist'] as $file => $fa) {
  42176. if (empty($fa['role'])) {
  42177. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILEROLE,
  42178. array('file' => $file, 'roles' => PEAR_Common::getFileRoles()));
  42179. continue;
  42180. } elseif (!in_array($fa['role'], PEAR_Common::getFileRoles())) {
  42181. $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE,
  42182. array('file' => $file, 'role' => $fa['role'], 'roles' => PEAR_Common::getFileRoles()));
  42183. }
  42184. if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', str_replace('\\', '/', $file))) {
  42185. // file contains .. parent directory or . cur directory references
  42186. $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
  42187. array('file' => $file));
  42188. }
  42189. if (isset($fa['install-as']) &&
  42190. preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
  42191. str_replace('\\', '/', $fa['install-as']))) {
  42192. // install-as contains .. parent directory or . cur directory references
  42193. $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
  42194. array('file' => $file . ' [installed as ' . $fa['install-as'] . ']'));
  42195. }
  42196. if (isset($fa['baseinstalldir']) &&
  42197. preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
  42198. str_replace('\\', '/', $fa['baseinstalldir']))) {
  42199. // install-as contains .. parent directory or . cur directory references
  42200. $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
  42201. array('file' => $file . ' [baseinstalldir ' . $fa['baseinstalldir'] . ']'));
  42202. }
  42203. }
  42204. }
  42205. if (isset($this->_registry) && $this->_isValid) {
  42206. $chan = $this->_registry->getChannel('pear.php.net');
  42207. if (PEAR::isError($chan)) {
  42208. $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $chan->getMessage());
  42209. return $this->_isValid = 0;
  42210. }
  42211. $validator = $chan->getValidationObject();
  42212. $validator->setPackageFile($this);
  42213. $validator->validate($state);
  42214. $failures = $validator->getFailures();
  42215. foreach ($failures['errors'] as $error) {
  42216. $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $error);
  42217. }
  42218. foreach ($failures['warnings'] as $warning) {
  42219. $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $warning);
  42220. }
  42221. }
  42222. if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$nofilechecking) {
  42223. if ($this->_analyzePhpFiles()) {
  42224. $this->_isValid = true;
  42225. }
  42226. }
  42227. if ($this->_isValid) {
  42228. return $this->_isValid = $state;
  42229. }
  42230. return $this->_isValid = 0;
  42231. }
  42232. function _analyzePhpFiles()
  42233. {
  42234. if (!$this->_isValid) {
  42235. return false;
  42236. }
  42237. if (!isset($this->_packageFile)) {
  42238. return false;
  42239. }
  42240. $dir_prefix = dirname($this->_packageFile);
  42241. $common = new PEAR_Common;
  42242. $log = isset($this->_logger) ? array(&$this->_logger, 'log') :
  42243. array($common, 'log');
  42244. $info = $this->getFilelist();
  42245. foreach ($info as $file => $fa) {
  42246. if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
  42247. $this->_validateError(PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND,
  42248. array('file' => realpath($dir_prefix) . DIRECTORY_SEPARATOR . $file));
  42249. continue;
  42250. }
  42251. if ($fa['role'] == 'php' && $dir_prefix) {
  42252. call_user_func_array($log, array(1, "Analyzing $file"));
  42253. $srcinfo = $this->_analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
  42254. if ($srcinfo) {
  42255. $this->_buildProvidesArray($srcinfo);
  42256. }
  42257. }
  42258. }
  42259. $this->_packageName = $pn = $this->getPackage();
  42260. $pnl = strlen($pn);
  42261. if (isset($this->_packageInfo['provides'])) {
  42262. foreach ((array) $this->_packageInfo['provides'] as $key => $what) {
  42263. if (isset($what['explicit'])) {
  42264. // skip conformance checks if the provides entry is
  42265. // specified in the package.xml file
  42266. continue;
  42267. }
  42268. extract($what);
  42269. if ($type == 'class') {
  42270. if (!strncasecmp($name, $pn, $pnl)) {
  42271. continue;
  42272. }
  42273. $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX,
  42274. array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn));
  42275. } elseif ($type == 'function') {
  42276. if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
  42277. continue;
  42278. }
  42279. $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX,
  42280. array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn));
  42281. }
  42282. }
  42283. }
  42284. return $this->_isValid;
  42285. }
  42286. /**
  42287. * Get the default xml generator object
  42288. *
  42289. * @return PEAR_PackageFile_Generator_v1
  42290. */
  42291. function &getDefaultGenerator()
  42292. {
  42293. if (!class_exists('PEAR_PackageFile_Generator_v1')) {
  42294. require_once 'PEAR/PackageFile/Generator/v1.php';
  42295. }
  42296. $a = new PEAR_PackageFile_Generator_v1($this);
  42297. return $a;
  42298. }
  42299. /**
  42300. * Get the contents of a file listed within the package.xml
  42301. * @param string
  42302. * @return string
  42303. */
  42304. function getFileContents($file)
  42305. {
  42306. if ($this->_archiveFile == $this->_packageFile) { // unpacked
  42307. $dir = dirname($this->_packageFile);
  42308. $file = $dir . DIRECTORY_SEPARATOR . $file;
  42309. $file = str_replace(array('/', '\\'),
  42310. array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
  42311. if (file_exists($file) && is_readable($file)) {
  42312. return implode('', file($file));
  42313. }
  42314. } else { // tgz
  42315. if (!class_exists('Archive_Tar')) {
  42316. require_once 'Archive/Tar.php';
  42317. }
  42318. $tar = new Archive_Tar($this->_archiveFile);
  42319. $tar->pushErrorHandling(PEAR_ERROR_RETURN);
  42320. if ($file != 'package.xml' && $file != 'package2.xml') {
  42321. $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
  42322. }
  42323. $file = $tar->extractInString($file);
  42324. $tar->popErrorHandling();
  42325. if (PEAR::isError($file)) {
  42326. return PEAR::raiseError("Cannot locate file '$file' in archive");
  42327. }
  42328. return $file;
  42329. }
  42330. }
  42331. // {{{ analyzeSourceCode()
  42332. /**
  42333. * Analyze the source code of the given PHP file
  42334. *
  42335. * @param string Filename of the PHP file
  42336. * @return mixed
  42337. * @access private
  42338. */
  42339. function _analyzeSourceCode($file)
  42340. {
  42341. if (!function_exists("token_get_all")) {
  42342. return false;
  42343. }
  42344. if (!defined('T_DOC_COMMENT')) {
  42345. define('T_DOC_COMMENT', T_COMMENT);
  42346. }
  42347. if (!defined('T_INTERFACE')) {
  42348. define('T_INTERFACE', -1);
  42349. }
  42350. if (!defined('T_IMPLEMENTS')) {
  42351. define('T_IMPLEMENTS', -1);
  42352. }
  42353. if (!$fp = @fopen($file, "r")) {
  42354. return false;
  42355. }
  42356. fclose($fp);
  42357. $contents = file_get_contents($file);
  42358. $tokens = token_get_all($contents);
  42359. /*
  42360. for ($i = 0; $i < sizeof($tokens); $i++) {
  42361. @list($token, $data) = $tokens[$i];
  42362. if (is_string($token)) {
  42363. var_dump($token);
  42364. } else {
  42365. print token_name($token) . ' ';
  42366. var_dump(rtrim($data));
  42367. }
  42368. }
  42369. */
  42370. $look_for = 0;
  42371. $paren_level = 0;
  42372. $bracket_level = 0;
  42373. $brace_level = 0;
  42374. $lastphpdoc = '';
  42375. $current_class = '';
  42376. $current_interface = '';
  42377. $current_class_level = -1;
  42378. $current_function = '';
  42379. $current_function_level = -1;
  42380. $declared_classes = array();
  42381. $declared_interfaces = array();
  42382. $declared_functions = array();
  42383. $declared_methods = array();
  42384. $used_classes = array();
  42385. $used_functions = array();
  42386. $extends = array();
  42387. $implements = array();
  42388. $nodeps = array();
  42389. $inquote = false;
  42390. $interface = false;
  42391. for ($i = 0; $i < sizeof($tokens); $i++) {
  42392. if (is_array($tokens[$i])) {
  42393. list($token, $data) = $tokens[$i];
  42394. } else {
  42395. $token = $tokens[$i];
  42396. $data = '';
  42397. }
  42398. if ($inquote) {
  42399. if ($token != '"' && $token != T_END_HEREDOC) {
  42400. continue;
  42401. } else {
  42402. $inquote = false;
  42403. continue;
  42404. }
  42405. }
  42406. switch ($token) {
  42407. case T_WHITESPACE:
  42408. break;
  42409. case ';':
  42410. if ($interface) {
  42411. $current_function = '';
  42412. $current_function_level = -1;
  42413. }
  42414. break;
  42415. case '"':
  42416. case T_START_HEREDOC:
  42417. $inquote = true;
  42418. break;
  42419. case T_CURLY_OPEN:
  42420. case T_DOLLAR_OPEN_CURLY_BRACES:
  42421. case '{': $brace_level++; continue 2;
  42422. case '}':
  42423. $brace_level--;
  42424. if ($current_class_level == $brace_level) {
  42425. $current_class = '';
  42426. $current_class_level = -1;
  42427. }
  42428. if ($current_function_level == $brace_level) {
  42429. $current_function = '';
  42430. $current_function_level = -1;
  42431. }
  42432. continue 2;
  42433. case '[': $bracket_level++; continue 2;
  42434. case ']': $bracket_level--; continue 2;
  42435. case '(': $paren_level++; continue 2;
  42436. case ')': $paren_level--; continue 2;
  42437. case T_INTERFACE:
  42438. $interface = true;
  42439. case T_CLASS:
  42440. if (($current_class_level != -1) || ($current_function_level != -1)) {
  42441. $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE,
  42442. array('file' => $file));
  42443. return false;
  42444. }
  42445. case T_FUNCTION:
  42446. case T_NEW:
  42447. case T_EXTENDS:
  42448. case T_IMPLEMENTS:
  42449. $look_for = $token;
  42450. continue 2;
  42451. case T_STRING:
  42452. if ($look_for == T_CLASS) {
  42453. $current_class = $data;
  42454. $current_class_level = $brace_level;
  42455. $declared_classes[] = $current_class;
  42456. } elseif ($look_for == T_INTERFACE) {
  42457. $current_interface = $data;
  42458. $current_class_level = $brace_level;
  42459. $declared_interfaces[] = $current_interface;
  42460. } elseif ($look_for == T_IMPLEMENTS) {
  42461. $implements[$current_class] = $data;
  42462. } elseif ($look_for == T_EXTENDS) {
  42463. $extends[$current_class] = $data;
  42464. } elseif ($look_for == T_FUNCTION) {
  42465. if ($current_class) {
  42466. $current_function = "$current_class::$data";
  42467. $declared_methods[$current_class][] = $data;
  42468. } elseif ($current_interface) {
  42469. $current_function = "$current_interface::$data";
  42470. $declared_methods[$current_interface][] = $data;
  42471. } else {
  42472. $current_function = $data;
  42473. $declared_functions[] = $current_function;
  42474. }
  42475. $current_function_level = $brace_level;
  42476. $m = array();
  42477. } elseif ($look_for == T_NEW) {
  42478. $used_classes[$data] = true;
  42479. }
  42480. $look_for = 0;
  42481. continue 2;
  42482. case T_VARIABLE:
  42483. $look_for = 0;
  42484. continue 2;
  42485. case T_DOC_COMMENT:
  42486. case T_COMMENT:
  42487. if (preg_match('!^/\*\*\s!', $data)) {
  42488. $lastphpdoc = $data;
  42489. if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
  42490. $nodeps = array_merge($nodeps, $m[1]);
  42491. }
  42492. }
  42493. continue 2;
  42494. case T_DOUBLE_COLON:
  42495. if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
  42496. $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE,
  42497. array('file' => $file));
  42498. return false;
  42499. }
  42500. $class = $tokens[$i - 1][1];
  42501. if (strtolower($class) != 'parent') {
  42502. $used_classes[$class] = true;
  42503. }
  42504. continue 2;
  42505. }
  42506. }
  42507. return array(
  42508. "source_file" => $file,
  42509. "declared_classes" => $declared_classes,
  42510. "declared_interfaces" => $declared_interfaces,
  42511. "declared_methods" => $declared_methods,
  42512. "declared_functions" => $declared_functions,
  42513. "used_classes" => array_diff(array_keys($used_classes), $nodeps),
  42514. "inheritance" => $extends,
  42515. "implements" => $implements,
  42516. );
  42517. }
  42518. /**
  42519. * Build a "provides" array from data returned by
  42520. * analyzeSourceCode(). The format of the built array is like
  42521. * this:
  42522. *
  42523. * array(
  42524. * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
  42525. * ...
  42526. * )
  42527. *
  42528. *
  42529. * @param array $srcinfo array with information about a source file
  42530. * as returned by the analyzeSourceCode() method.
  42531. *
  42532. * @return void
  42533. *
  42534. * @access private
  42535. *
  42536. */
  42537. function _buildProvidesArray($srcinfo)
  42538. {
  42539. if (!$this->_isValid) {
  42540. return false;
  42541. }
  42542. $file = basename($srcinfo['source_file']);
  42543. $pn = $this->getPackage();
  42544. $pnl = strlen($pn);
  42545. foreach ($srcinfo['declared_classes'] as $class) {
  42546. $key = "class;$class";
  42547. if (isset($this->_packageInfo['provides'][$key])) {
  42548. continue;
  42549. }
  42550. $this->_packageInfo['provides'][$key] =
  42551. array('file'=> $file, 'type' => 'class', 'name' => $class);
  42552. if (isset($srcinfo['inheritance'][$class])) {
  42553. $this->_packageInfo['provides'][$key]['extends'] =
  42554. $srcinfo['inheritance'][$class];
  42555. }
  42556. }
  42557. foreach ($srcinfo['declared_methods'] as $class => $methods) {
  42558. foreach ($methods as $method) {
  42559. $function = "$class::$method";
  42560. $key = "function;$function";
  42561. if ($method[0] == '_' || !strcasecmp($method, $class) ||
  42562. isset($this->_packageInfo['provides'][$key])) {
  42563. continue;
  42564. }
  42565. $this->_packageInfo['provides'][$key] =
  42566. array('file'=> $file, 'type' => 'function', 'name' => $function);
  42567. }
  42568. }
  42569. foreach ($srcinfo['declared_functions'] as $function) {
  42570. $key = "function;$function";
  42571. if ($function[0] == '_' || isset($this->_packageInfo['provides'][$key])) {
  42572. continue;
  42573. }
  42574. if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
  42575. $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
  42576. }
  42577. $this->_packageInfo['provides'][$key] =
  42578. array('file'=> $file, 'type' => 'function', 'name' => $function);
  42579. }
  42580. }
  42581. // }}}
  42582. }
  42583. ?>
  42584. �����������������������������������������������������������������������������PEAR-1.10.16/PEAR/PackageFile/v2.php����������������������������������������������������������������0000664�0001750�0001750�00000210500�14720722517�016210� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  42585. /**
  42586. * PEAR_PackageFile_v2, package.xml version 2.0
  42587. *
  42588. * PHP versions 4 and 5
  42589. *
  42590. * @category pear
  42591. * @package PEAR
  42592. * @author Greg Beaver <cellog@php.net>
  42593. * @copyright 1997-2009 The Authors
  42594. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  42595. * @link http://pear.php.net/package/PEAR
  42596. * @since File available since Release 1.4.0a1
  42597. */
  42598. /**
  42599. * For error handling
  42600. */
  42601. require_once 'PEAR/ErrorStack.php';
  42602. /**
  42603. * @category pear
  42604. * @package PEAR
  42605. * @author Greg Beaver <cellog@php.net>
  42606. * @copyright 1997-2009 The Authors
  42607. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  42608. * @version Release: 1.10.16
  42609. * @link http://pear.php.net/package/PEAR
  42610. * @since Class available since Release 1.4.0a1
  42611. */
  42612. class PEAR_PackageFile_v2
  42613. {
  42614. /**
  42615. * Parsed package information
  42616. * @var array
  42617. * @access private
  42618. */
  42619. var $_packageInfo = array();
  42620. /**
  42621. * path to package .tgz or false if this is a local/extracted package.xml
  42622. * @var string|false
  42623. * @access private
  42624. */
  42625. var $_archiveFile;
  42626. /**
  42627. * path to package .xml or false if this is an abstract parsed-from-string xml
  42628. * @var string|false
  42629. * @access private
  42630. */
  42631. var $_packageFile;
  42632. /**
  42633. * This is used by file analysis routines to log progress information
  42634. * @var PEAR_Common
  42635. * @access protected
  42636. */
  42637. var $_logger;
  42638. /**
  42639. * This is set to the highest validation level that has been validated
  42640. *
  42641. * If the package.xml is invalid or unknown, this is set to 0. If
  42642. * normal validation has occurred, this is set to PEAR_VALIDATE_NORMAL. If
  42643. * downloading/installation validation has occurred it is set to PEAR_VALIDATE_DOWNLOADING
  42644. * or INSTALLING, and so on up to PEAR_VALIDATE_PACKAGING. This allows validation
  42645. * "caching" to occur, which is particularly important for package validation, so
  42646. * that PHP files are not validated twice
  42647. * @var int
  42648. * @access private
  42649. */
  42650. var $_isValid = 0;
  42651. /**
  42652. * True if the filelist has been validated
  42653. * @param bool
  42654. */
  42655. var $_filesValid = false;
  42656. /**
  42657. * @var PEAR_Registry
  42658. * @access protected
  42659. */
  42660. var $_registry;
  42661. /**
  42662. * @var PEAR_Config
  42663. * @access protected
  42664. */
  42665. var $_config;
  42666. /**
  42667. * Optional Dependency group requested for installation
  42668. * @var string
  42669. * @access private
  42670. */
  42671. var $_requestedGroup = false;
  42672. /**
  42673. * @var PEAR_ErrorStack
  42674. * @access protected
  42675. */
  42676. var $_stack;
  42677. /**
  42678. * Namespace prefix used for tasks in this package.xml - use tasks: whenever possible
  42679. */
  42680. var $_tasksNs;
  42681. /**
  42682. * Determines whether this packagefile was initialized only with partial package info
  42683. *
  42684. * If this package file was constructed via parsing REST, it will only contain
  42685. *
  42686. * - package name
  42687. * - channel name
  42688. * - dependencies
  42689. * @var boolean
  42690. * @access private
  42691. */
  42692. var $_incomplete = true;
  42693. /**
  42694. * @var PEAR_PackageFile_v2_Validator
  42695. */
  42696. var $_v2Validator;
  42697. /**
  42698. * The constructor merely sets up the private error stack
  42699. */
  42700. function __construct()
  42701. {
  42702. $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v2', false, null);
  42703. $this->_isValid = false;
  42704. }
  42705. /**
  42706. * PHP 4 style constructor for backwards compatibility.
  42707. * Used by PEAR_PackageFileManager2
  42708. */
  42709. public function PEAR_PackageFile_v2()
  42710. {
  42711. $this->__construct();
  42712. }
  42713. /**
  42714. * To make unit-testing easier
  42715. * @param PEAR_Frontend_*
  42716. * @param array options
  42717. * @param PEAR_Config
  42718. * @return PEAR_Downloader
  42719. * @access protected
  42720. */
  42721. function &getPEARDownloader(&$i, $o, &$c)
  42722. {
  42723. $z = new PEAR_Downloader($i, $o, $c);
  42724. return $z;
  42725. }
  42726. /**
  42727. * To make unit-testing easier
  42728. * @param PEAR_Config
  42729. * @param array options
  42730. * @param array package name as returned from {@link PEAR_Registry::parsePackageName()}
  42731. * @param int PEAR_VALIDATE_* constant
  42732. * @return PEAR_Dependency2
  42733. * @access protected
  42734. */
  42735. function &getPEARDependency2(&$c, $o, $p, $s = PEAR_VALIDATE_INSTALLING)
  42736. {
  42737. if (!class_exists('PEAR_Dependency2')) {
  42738. require_once 'PEAR/Dependency2.php';
  42739. }
  42740. $z = new PEAR_Dependency2($c, $o, $p, $s);
  42741. return $z;
  42742. }
  42743. function getInstalledBinary()
  42744. {
  42745. return isset($this->_packageInfo['#binarypackage']) ? $this->_packageInfo['#binarypackage'] :
  42746. false;
  42747. }
  42748. /**
  42749. * Installation of source package has failed, attempt to download and install the
  42750. * binary version of this package.
  42751. * @param PEAR_Installer
  42752. * @return array|false
  42753. */
  42754. function installBinary(&$installer)
  42755. {
  42756. if (!OS_WINDOWS) {
  42757. $a = false;
  42758. return $a;
  42759. }
  42760. if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
  42761. $releasetype = $this->getPackageType() . 'release';
  42762. if (!is_array($installer->getInstallPackages())) {
  42763. $a = false;
  42764. return $a;
  42765. }
  42766. foreach ($installer->getInstallPackages() as $p) {
  42767. if ($p->isExtension($this->_packageInfo['providesextension'])) {
  42768. if ($p->getPackageType() != 'extsrc' && $p->getPackageType() != 'zendextsrc') {
  42769. $a = false;
  42770. return $a; // the user probably downloaded it separately
  42771. }
  42772. }
  42773. }
  42774. if (isset($this->_packageInfo[$releasetype]['binarypackage'])) {
  42775. $installer->log(0, 'Attempting to download binary version of extension "' .
  42776. $this->_packageInfo['providesextension'] . '"');
  42777. $params = $this->_packageInfo[$releasetype]['binarypackage'];
  42778. if (!is_array($params) || !isset($params[0])) {
  42779. $params = array($params);
  42780. }
  42781. if (isset($this->_packageInfo['channel'])) {
  42782. foreach ($params as $i => $param) {
  42783. $params[$i] = array('channel' => $this->_packageInfo['channel'],
  42784. 'package' => $param, 'version' => $this->getVersion());
  42785. }
  42786. }
  42787. $dl = &$this->getPEARDownloader($installer->ui, $installer->getOptions(),
  42788. $installer->config);
  42789. $verbose = $dl->config->get('verbose');
  42790. $dl->config->set('verbose', -1);
  42791. foreach ($params as $param) {
  42792. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  42793. $ret = $dl->download(array($param));
  42794. PEAR::popErrorHandling();
  42795. if (is_array($ret) && count($ret)) {
  42796. break;
  42797. }
  42798. }
  42799. $dl->config->set('verbose', $verbose);
  42800. if (is_array($ret)) {
  42801. if (count($ret) == 1) {
  42802. $pf = $ret[0]->getPackageFile();
  42803. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  42804. $err = $installer->install($ret[0]);
  42805. PEAR::popErrorHandling();
  42806. if (is_array($err)) {
  42807. $this->_packageInfo['#binarypackage'] = $ret[0]->getPackage();
  42808. // "install" self, so all dependencies will work transparently
  42809. $this->_registry->addPackage2($this);
  42810. $installer->log(0, 'Download and install of binary extension "' .
  42811. $this->_registry->parsedPackageNameToString(
  42812. array('channel' => $pf->getChannel(),
  42813. 'package' => $pf->getPackage()), true) . '" successful');
  42814. $a = array($ret[0], $err);
  42815. return $a;
  42816. }
  42817. $installer->log(0, 'Download and install of binary extension "' .
  42818. $this->_registry->parsedPackageNameToString(
  42819. array('channel' => $pf->getChannel(),
  42820. 'package' => $pf->getPackage()), true) . '" failed');
  42821. }
  42822. }
  42823. }
  42824. }
  42825. $a = false;
  42826. return $a;
  42827. }
  42828. /**
  42829. * @return string|false Extension name
  42830. */
  42831. function getProvidesExtension()
  42832. {
  42833. if (in_array($this->getPackageType(),
  42834. array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
  42835. if (isset($this->_packageInfo['providesextension'])) {
  42836. return $this->_packageInfo['providesextension'];
  42837. }
  42838. }
  42839. return false;
  42840. }
  42841. /**
  42842. * @param string Extension name
  42843. * @return bool
  42844. */
  42845. function isExtension($extension)
  42846. {
  42847. if (in_array($this->getPackageType(),
  42848. array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
  42849. return $this->_packageInfo['providesextension'] == $extension;
  42850. }
  42851. return false;
  42852. }
  42853. /**
  42854. * Tests whether every part of the package.xml 1.0 is represented in
  42855. * this package.xml 2.0
  42856. * @param PEAR_PackageFile_v1
  42857. * @return bool
  42858. */
  42859. function isEquivalent($pf1)
  42860. {
  42861. if (!$pf1) {
  42862. return true;
  42863. }
  42864. if ($this->getPackageType() == 'bundle') {
  42865. return false;
  42866. }
  42867. $this->_stack->getErrors(true);
  42868. if (!$pf1->validate(PEAR_VALIDATE_NORMAL)) {
  42869. return false;
  42870. }
  42871. $pass = true;
  42872. if ($pf1->getPackage() != $this->getPackage()) {
  42873. $this->_differentPackage($pf1->getPackage());
  42874. $pass = false;
  42875. }
  42876. if ($pf1->getVersion() != $this->getVersion()) {
  42877. $this->_differentVersion($pf1->getVersion());
  42878. $pass = false;
  42879. }
  42880. if (trim($pf1->getSummary()) != $this->getSummary()) {
  42881. $this->_differentSummary($pf1->getSummary());
  42882. $pass = false;
  42883. }
  42884. if (preg_replace('/\s+/', '', $pf1->getDescription()) !=
  42885. preg_replace('/\s+/', '', $this->getDescription())) {
  42886. $this->_differentDescription($pf1->getDescription());
  42887. $pass = false;
  42888. }
  42889. if ($pf1->getState() != $this->getState()) {
  42890. $this->_differentState($pf1->getState());
  42891. $pass = false;
  42892. }
  42893. if (!strstr(preg_replace('/\s+/', '', $this->getNotes()),
  42894. preg_replace('/\s+/', '', $pf1->getNotes()))) {
  42895. $this->_differentNotes($pf1->getNotes());
  42896. $pass = false;
  42897. }
  42898. $mymaintainers = $this->getMaintainers();
  42899. $yourmaintainers = $pf1->getMaintainers();
  42900. for ($i1 = 0; $i1 < count($yourmaintainers); $i1++) {
  42901. $reset = false;
  42902. for ($i2 = 0; $i2 < count($mymaintainers); $i2++) {
  42903. if ($mymaintainers[$i2]['handle'] == $yourmaintainers[$i1]['handle']) {
  42904. if ($mymaintainers[$i2]['role'] != $yourmaintainers[$i1]['role']) {
  42905. $this->_differentRole($mymaintainers[$i2]['handle'],
  42906. $yourmaintainers[$i1]['role'], $mymaintainers[$i2]['role']);
  42907. $pass = false;
  42908. }
  42909. if ($mymaintainers[$i2]['email'] != $yourmaintainers[$i1]['email']) {
  42910. $this->_differentEmail($mymaintainers[$i2]['handle'],
  42911. $yourmaintainers[$i1]['email'], $mymaintainers[$i2]['email']);
  42912. $pass = false;
  42913. }
  42914. if ($mymaintainers[$i2]['name'] != $yourmaintainers[$i1]['name']) {
  42915. $this->_differentName($mymaintainers[$i2]['handle'],
  42916. $yourmaintainers[$i1]['name'], $mymaintainers[$i2]['name']);
  42917. $pass = false;
  42918. }
  42919. unset($mymaintainers[$i2]);
  42920. $mymaintainers = array_values($mymaintainers);
  42921. unset($yourmaintainers[$i1]);
  42922. $yourmaintainers = array_values($yourmaintainers);
  42923. $reset = true;
  42924. break;
  42925. }
  42926. }
  42927. if ($reset) {
  42928. $i1 = -1;
  42929. }
  42930. }
  42931. $this->_unmatchedMaintainers($mymaintainers, $yourmaintainers);
  42932. $filelist = $this->getFilelist();
  42933. foreach ($pf1->getFilelist() as $file => $atts) {
  42934. if (!isset($filelist[$file])) {
  42935. $this->_missingFile($file);
  42936. $pass = false;
  42937. }
  42938. }
  42939. return $pass;
  42940. }
  42941. function _differentPackage($package)
  42942. {
  42943. $this->_stack->push(__FUNCTION__, 'error', array('package' => $package,
  42944. 'self' => $this->getPackage()),
  42945. 'package.xml 1.0 package "%package%" does not match "%self%"');
  42946. }
  42947. function _differentVersion($version)
  42948. {
  42949. $this->_stack->push(__FUNCTION__, 'error', array('version' => $version,
  42950. 'self' => $this->getVersion()),
  42951. 'package.xml 1.0 version "%version%" does not match "%self%"');
  42952. }
  42953. function _differentState($state)
  42954. {
  42955. $this->_stack->push(__FUNCTION__, 'error', array('state' => $state,
  42956. 'self' => $this->getState()),
  42957. 'package.xml 1.0 state "%state%" does not match "%self%"');
  42958. }
  42959. function _differentRole($handle, $role, $selfrole)
  42960. {
  42961. $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
  42962. 'role' => $role, 'self' => $selfrole),
  42963. 'package.xml 1.0 maintainer "%handle%" role "%role%" does not match "%self%"');
  42964. }
  42965. function _differentEmail($handle, $email, $selfemail)
  42966. {
  42967. $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
  42968. 'email' => $email, 'self' => $selfemail),
  42969. 'package.xml 1.0 maintainer "%handle%" email "%email%" does not match "%self%"');
  42970. }
  42971. function _differentName($handle, $name, $selfname)
  42972. {
  42973. $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
  42974. 'name' => $name, 'self' => $selfname),
  42975. 'package.xml 1.0 maintainer "%handle%" name "%name%" does not match "%self%"');
  42976. }
  42977. function _unmatchedMaintainers($my, $yours)
  42978. {
  42979. if ($my) {
  42980. array_walk($my, function(&$i, $k) { $i = $i["handle"]; });
  42981. $this->_stack->push(__FUNCTION__, 'error', array('handles' => $my),
  42982. 'package.xml 2.0 has unmatched extra maintainers "%handles%"');
  42983. }
  42984. if ($yours) {
  42985. array_walk($yours, function(&$i, $k) { $i = $i["handle"]; });
  42986. $this->_stack->push(__FUNCTION__, 'error', array('handles' => $yours),
  42987. 'package.xml 1.0 has unmatched extra maintainers "%handles%"');
  42988. }
  42989. }
  42990. function _differentNotes($notes)
  42991. {
  42992. $truncnotes = strlen($notes) < 25 ? $notes : substr($notes, 0, 24) . '...';
  42993. $truncmynotes = strlen($this->getNotes()) < 25 ? $this->getNotes() :
  42994. substr($this->getNotes(), 0, 24) . '...';
  42995. $this->_stack->push(__FUNCTION__, 'error', array('notes' => $truncnotes,
  42996. 'self' => $truncmynotes),
  42997. 'package.xml 1.0 release notes "%notes%" do not match "%self%"');
  42998. }
  42999. function _differentSummary($summary)
  43000. {
  43001. $truncsummary = strlen($summary) < 25 ? $summary : substr($summary, 0, 24) . '...';
  43002. $truncmysummary = strlen($this->getsummary()) < 25 ? $this->getSummary() :
  43003. substr($this->getsummary(), 0, 24) . '...';
  43004. $this->_stack->push(__FUNCTION__, 'error', array('summary' => $truncsummary,
  43005. 'self' => $truncmysummary),
  43006. 'package.xml 1.0 summary "%summary%" does not match "%self%"');
  43007. }
  43008. function _differentDescription($description)
  43009. {
  43010. $truncdescription = trim(strlen($description) < 25 ? $description : substr($description, 0, 24) . '...');
  43011. $truncmydescription = trim(strlen($this->getDescription()) < 25 ? $this->getDescription() :
  43012. substr($this->getdescription(), 0, 24) . '...');
  43013. $this->_stack->push(__FUNCTION__, 'error', array('description' => $truncdescription,
  43014. 'self' => $truncmydescription),
  43015. 'package.xml 1.0 description "%description%" does not match "%self%"');
  43016. }
  43017. function _missingFile($file)
  43018. {
  43019. $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
  43020. 'package.xml 1.0 file "%file%" is not present in <contents>');
  43021. }
  43022. /**
  43023. * WARNING - do not use this function unless you know what you're doing
  43024. */
  43025. function setRawState($state)
  43026. {
  43027. if (!isset($this->_packageInfo['stability'])) {
  43028. $this->_packageInfo['stability'] = array();
  43029. }
  43030. $this->_packageInfo['stability']['release'] = $state;
  43031. }
  43032. /**
  43033. * WARNING - do not use this function unless you know what you're doing
  43034. */
  43035. function setRawCompatible($compatible)
  43036. {
  43037. $this->_packageInfo['compatible'] = $compatible;
  43038. }
  43039. /**
  43040. * WARNING - do not use this function unless you know what you're doing
  43041. */
  43042. function setRawPackage($package)
  43043. {
  43044. $this->_packageInfo['name'] = $package;
  43045. }
  43046. /**
  43047. * WARNING - do not use this function unless you know what you're doing
  43048. */
  43049. function setRawChannel($channel)
  43050. {
  43051. $this->_packageInfo['channel'] = $channel;
  43052. }
  43053. function setRequestedGroup($group)
  43054. {
  43055. $this->_requestedGroup = $group;
  43056. }
  43057. function getRequestedGroup()
  43058. {
  43059. if (isset($this->_requestedGroup)) {
  43060. return $this->_requestedGroup;
  43061. }
  43062. return false;
  43063. }
  43064. /**
  43065. * For saving in the registry.
  43066. *
  43067. * Set the last version that was installed
  43068. * @param string
  43069. */
  43070. function setLastInstalledVersion($version)
  43071. {
  43072. $this->_packageInfo['_lastversion'] = $version;
  43073. }
  43074. /**
  43075. * @return string|false
  43076. */
  43077. function getLastInstalledVersion()
  43078. {
  43079. if (isset($this->_packageInfo['_lastversion'])) {
  43080. return $this->_packageInfo['_lastversion'];
  43081. }
  43082. return false;
  43083. }
  43084. /**
  43085. * Determines whether this package.xml has post-install scripts or not
  43086. * @return array|false
  43087. */
  43088. function listPostinstallScripts()
  43089. {
  43090. $filelist = $this->getFilelist();
  43091. $contents = $this->getContents();
  43092. $contents = $contents['dir']['file'];
  43093. if (!is_array($contents) || !isset($contents[0])) {
  43094. $contents = array($contents);
  43095. }
  43096. $taskfiles = array();
  43097. foreach ($contents as $file) {
  43098. $atts = $file['attribs'];
  43099. unset($file['attribs']);
  43100. if (count($file)) {
  43101. $taskfiles[$atts['name']] = $file;
  43102. }
  43103. }
  43104. $common = new PEAR_Common;
  43105. $common->debug = $this->_config->get('verbose');
  43106. $this->_scripts = array();
  43107. $ret = array();
  43108. foreach ($taskfiles as $name => $tasks) {
  43109. if (!isset($filelist[$name])) {
  43110. // ignored files will not be in the filelist
  43111. continue;
  43112. }
  43113. $atts = $filelist[$name];
  43114. foreach ($tasks as $tag => $raw) {
  43115. $task = $this->getTask($tag);
  43116. $task = new $task($this->_config, $common, PEAR_TASK_INSTALL);
  43117. if ($task->isScript()) {
  43118. $ret[] = $filelist[$name]['installed_as'];
  43119. }
  43120. }
  43121. }
  43122. if (count($ret)) {
  43123. return $ret;
  43124. }
  43125. return false;
  43126. }
  43127. /**
  43128. * Initialize post-install scripts for running
  43129. *
  43130. * This method can be used to detect post-install scripts, as the return value
  43131. * indicates whether any exist
  43132. * @return bool
  43133. */
  43134. function initPostinstallScripts()
  43135. {
  43136. $filelist = $this->getFilelist();
  43137. $contents = $this->getContents();
  43138. $contents = $contents['dir']['file'];
  43139. if (!is_array($contents) || !isset($contents[0])) {
  43140. $contents = array($contents);
  43141. }
  43142. $taskfiles = array();
  43143. foreach ($contents as $file) {
  43144. $atts = $file['attribs'];
  43145. unset($file['attribs']);
  43146. if (count($file)) {
  43147. $taskfiles[$atts['name']] = $file;
  43148. }
  43149. }
  43150. $common = new PEAR_Common;
  43151. $common->debug = $this->_config->get('verbose');
  43152. $this->_scripts = array();
  43153. foreach ($taskfiles as $name => $tasks) {
  43154. if (!isset($filelist[$name])) {
  43155. // file was not installed due to installconditions
  43156. continue;
  43157. }
  43158. $atts = $filelist[$name];
  43159. foreach ($tasks as $tag => $raw) {
  43160. $taskname = $this->getTask($tag);
  43161. $task = new $taskname($this->_config, $common, PEAR_TASK_INSTALL);
  43162. if (!$task->isScript()) {
  43163. continue; // scripts are only handled after installation
  43164. }
  43165. $lastversion = isset($this->_packageInfo['_lastversion']) ?
  43166. $this->_packageInfo['_lastversion'] : null;
  43167. $task->init($raw, $atts, $lastversion);
  43168. $res = $task->startSession($this, $atts['installed_as'], null);
  43169. if (!$res) {
  43170. continue; // skip this file
  43171. }
  43172. if (PEAR::isError($res)) {
  43173. return $res;
  43174. }
  43175. $this->_scripts[] = $task;
  43176. }
  43177. }
  43178. if (count($this->_scripts)) {
  43179. return true;
  43180. }
  43181. return false;
  43182. }
  43183. function runPostinstallScripts()
  43184. {
  43185. if ($this->initPostinstallScripts()) {
  43186. $ui = &PEAR_Frontend::singleton();
  43187. if ($ui) {
  43188. $ui->runPostinstallScripts($this->_scripts, $this);
  43189. }
  43190. }
  43191. }
  43192. /**
  43193. * Convert a recursive set of <dir> and <file> tags into a single <dir> tag with
  43194. * <file> tags.
  43195. */
  43196. function flattenFilelist()
  43197. {
  43198. if (isset($this->_packageInfo['bundle'])) {
  43199. return;
  43200. }
  43201. $filelist = array();
  43202. if (isset($this->_packageInfo['contents']['dir']['dir'])) {
  43203. $this->_getFlattenedFilelist($filelist, $this->_packageInfo['contents']['dir']);
  43204. if (!isset($filelist[1])) {
  43205. $filelist = $filelist[0];
  43206. }
  43207. $this->_packageInfo['contents']['dir']['file'] = $filelist;
  43208. unset($this->_packageInfo['contents']['dir']['dir']);
  43209. } else {
  43210. // else already flattened but check for baseinstalldir propagation
  43211. if (isset($this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'])) {
  43212. if (isset($this->_packageInfo['contents']['dir']['file'][0])) {
  43213. foreach ($this->_packageInfo['contents']['dir']['file'] as $i => $file) {
  43214. if (isset($file['attribs']['baseinstalldir'])) {
  43215. continue;
  43216. }
  43217. $this->_packageInfo['contents']['dir']['file'][$i]['attribs']['baseinstalldir']
  43218. = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
  43219. }
  43220. } else {
  43221. if (!isset($this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'])) {
  43222. $this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir']
  43223. = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
  43224. }
  43225. }
  43226. }
  43227. }
  43228. }
  43229. /**
  43230. * @param array the final flattened file list
  43231. * @param array the current directory being processed
  43232. * @param string|false any recursively inherited baeinstalldir attribute
  43233. * @param string private recursion variable
  43234. * @return array
  43235. * @access protected
  43236. */
  43237. function _getFlattenedFilelist(&$files, $dir, $baseinstall = false, $path = '')
  43238. {
  43239. if (isset($dir['attribs']) && isset($dir['attribs']['baseinstalldir'])) {
  43240. $baseinstall = $dir['attribs']['baseinstalldir'];
  43241. }
  43242. if (isset($dir['dir'])) {
  43243. if (!isset($dir['dir'][0])) {
  43244. $dir['dir'] = array($dir['dir']);
  43245. }
  43246. foreach ($dir['dir'] as $subdir) {
  43247. if (!isset($subdir['attribs']) || !isset($subdir['attribs']['name'])) {
  43248. $name = '*unknown*';
  43249. } else {
  43250. $name = $subdir['attribs']['name'];
  43251. }
  43252. $newpath = empty($path) ? $name :
  43253. $path . '/' . $name;
  43254. $this->_getFlattenedFilelist($files, $subdir,
  43255. $baseinstall, $newpath);
  43256. }
  43257. }
  43258. if (isset($dir['file'])) {
  43259. if (!isset($dir['file'][0])) {
  43260. $dir['file'] = array($dir['file']);
  43261. }
  43262. foreach ($dir['file'] as $file) {
  43263. $attrs = $file['attribs'];
  43264. $name = $attrs['name'];
  43265. if ($baseinstall && !isset($attrs['baseinstalldir'])) {
  43266. $attrs['baseinstalldir'] = $baseinstall;
  43267. }
  43268. $attrs['name'] = empty($path) ? $name : $path . '/' . $name;
  43269. $attrs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
  43270. $attrs['name']);
  43271. $file['attribs'] = $attrs;
  43272. $files[] = $file;
  43273. }
  43274. }
  43275. }
  43276. function setConfig(&$config)
  43277. {
  43278. $this->_config = &$config;
  43279. $this->_registry = &$config->getRegistry();
  43280. }
  43281. function setLogger(&$logger)
  43282. {
  43283. if (!is_object($logger) || !method_exists($logger, 'log')) {
  43284. return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
  43285. }
  43286. $this->_logger = &$logger;
  43287. }
  43288. /**
  43289. * WARNING - do not use this function directly unless you know what you're doing
  43290. */
  43291. function setDeps($deps)
  43292. {
  43293. $this->_packageInfo['dependencies'] = $deps;
  43294. }
  43295. /**
  43296. * WARNING - do not use this function directly unless you know what you're doing
  43297. */
  43298. function setCompatible($compat)
  43299. {
  43300. $this->_packageInfo['compatible'] = $compat;
  43301. }
  43302. function setPackagefile($file, $archive = false)
  43303. {
  43304. $this->_packageFile = $file;
  43305. $this->_archiveFile = $archive ? $archive : $file;
  43306. }
  43307. /**
  43308. * Wrapper to {@link PEAR_ErrorStack::getErrors()}
  43309. * @param boolean determines whether to purge the error stack after retrieving
  43310. * @return array
  43311. */
  43312. function getValidationWarnings($purge = true)
  43313. {
  43314. return $this->_stack->getErrors($purge);
  43315. }
  43316. function getPackageFile()
  43317. {
  43318. return $this->_packageFile;
  43319. }
  43320. function getArchiveFile()
  43321. {
  43322. return $this->_archiveFile;
  43323. }
  43324. /**
  43325. * Directly set the array that defines this packagefile
  43326. *
  43327. * WARNING: no validation. This should only be performed by internal methods
  43328. * inside PEAR or by inputting an array saved from an existing PEAR_PackageFile_v2
  43329. * @param array
  43330. */
  43331. function fromArray($pinfo)
  43332. {
  43333. unset($pinfo['old']);
  43334. unset($pinfo['xsdversion']);
  43335. // If the changelog isn't an array then it was passed in as an empty tag
  43336. if (isset($pinfo['changelog']) && !is_array($pinfo['changelog'])) {
  43337. unset($pinfo['changelog']);
  43338. }
  43339. $this->_incomplete = false;
  43340. $this->_packageInfo = $pinfo;
  43341. }
  43342. function isIncomplete()
  43343. {
  43344. return $this->_incomplete;
  43345. }
  43346. /**
  43347. * @return array
  43348. */
  43349. function toArray($forreg = false)
  43350. {
  43351. if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
  43352. return false;
  43353. }
  43354. return $this->getArray($forreg);
  43355. }
  43356. function getArray($forReg = false)
  43357. {
  43358. if ($forReg) {
  43359. $arr = $this->_packageInfo;
  43360. $arr['old'] = array();
  43361. $arr['old']['version'] = $this->getVersion();
  43362. $arr['old']['release_date'] = $this->getDate();
  43363. $arr['old']['release_state'] = $this->getState();
  43364. $arr['old']['release_license'] = $this->getLicense();
  43365. $arr['old']['release_notes'] = $this->getNotes();
  43366. $arr['old']['release_deps'] = $this->getDeps();
  43367. $arr['old']['maintainers'] = $this->getMaintainers();
  43368. $arr['xsdversion'] = '2.0';
  43369. return $arr;
  43370. } else {
  43371. $info = $this->_packageInfo;
  43372. unset($info['dirtree']);
  43373. if (isset($info['_lastversion'])) {
  43374. unset($info['_lastversion']);
  43375. }
  43376. if (isset($info['#binarypackage'])) {
  43377. unset($info['#binarypackage']);
  43378. }
  43379. return $info;
  43380. }
  43381. }
  43382. function packageInfo($field)
  43383. {
  43384. $arr = $this->getArray(true);
  43385. if ($field == 'state') {
  43386. return $arr['stability']['release'];
  43387. }
  43388. if ($field == 'api-version') {
  43389. return $arr['version']['api'];
  43390. }
  43391. if ($field == 'api-state') {
  43392. return $arr['stability']['api'];
  43393. }
  43394. if (isset($arr['old'][$field])) {
  43395. if (!is_string($arr['old'][$field])) {
  43396. return null;
  43397. }
  43398. return $arr['old'][$field];
  43399. }
  43400. if (isset($arr[$field])) {
  43401. if (!is_string($arr[$field])) {
  43402. return null;
  43403. }
  43404. return $arr[$field];
  43405. }
  43406. return null;
  43407. }
  43408. function getName()
  43409. {
  43410. return $this->getPackage();
  43411. }
  43412. function getPackage()
  43413. {
  43414. if (isset($this->_packageInfo['name'])) {
  43415. return $this->_packageInfo['name'];
  43416. }
  43417. return false;
  43418. }
  43419. function getChannel()
  43420. {
  43421. if (isset($this->_packageInfo['uri'])) {
  43422. return '__uri';
  43423. }
  43424. if (isset($this->_packageInfo['channel'])) {
  43425. return strtolower($this->_packageInfo['channel']);
  43426. }
  43427. return false;
  43428. }
  43429. function getUri()
  43430. {
  43431. if (isset($this->_packageInfo['uri'])) {
  43432. return $this->_packageInfo['uri'];
  43433. }
  43434. return false;
  43435. }
  43436. function getExtends()
  43437. {
  43438. if (isset($this->_packageInfo['extends'])) {
  43439. return $this->_packageInfo['extends'];
  43440. }
  43441. return false;
  43442. }
  43443. function getSummary()
  43444. {
  43445. if (isset($this->_packageInfo['summary'])) {
  43446. return $this->_packageInfo['summary'];
  43447. }
  43448. return false;
  43449. }
  43450. function getDescription()
  43451. {
  43452. if (isset($this->_packageInfo['description'])) {
  43453. return $this->_packageInfo['description'];
  43454. }
  43455. return false;
  43456. }
  43457. function getMaintainers($raw = false)
  43458. {
  43459. if (!isset($this->_packageInfo['lead'])) {
  43460. return false;
  43461. }
  43462. if ($raw) {
  43463. $ret = array('lead' => $this->_packageInfo['lead']);
  43464. (isset($this->_packageInfo['developer'])) ?
  43465. $ret['developer'] = $this->_packageInfo['developer'] :null;
  43466. (isset($this->_packageInfo['contributor'])) ?
  43467. $ret['contributor'] = $this->_packageInfo['contributor'] :null;
  43468. (isset($this->_packageInfo['helper'])) ?
  43469. $ret['helper'] = $this->_packageInfo['helper'] :null;
  43470. return $ret;
  43471. } else {
  43472. $ret = array();
  43473. $leads = isset($this->_packageInfo['lead'][0]) ? $this->_packageInfo['lead'] :
  43474. array($this->_packageInfo['lead']);
  43475. foreach ($leads as $lead) {
  43476. $s = $lead;
  43477. $s['handle'] = $s['user'];
  43478. unset($s['user']);
  43479. $s['role'] = 'lead';
  43480. $ret[] = $s;
  43481. }
  43482. if (isset($this->_packageInfo['developer'])) {
  43483. $leads = isset($this->_packageInfo['developer'][0]) ?
  43484. $this->_packageInfo['developer'] :
  43485. array($this->_packageInfo['developer']);
  43486. foreach ($leads as $maintainer) {
  43487. $s = $maintainer;
  43488. $s['handle'] = $s['user'];
  43489. unset($s['user']);
  43490. $s['role'] = 'developer';
  43491. $ret[] = $s;
  43492. }
  43493. }
  43494. if (isset($this->_packageInfo['contributor'])) {
  43495. $leads = isset($this->_packageInfo['contributor'][0]) ?
  43496. $this->_packageInfo['contributor'] :
  43497. array($this->_packageInfo['contributor']);
  43498. foreach ($leads as $maintainer) {
  43499. $s = $maintainer;
  43500. $s['handle'] = $s['user'];
  43501. unset($s['user']);
  43502. $s['role'] = 'contributor';
  43503. $ret[] = $s;
  43504. }
  43505. }
  43506. if (isset($this->_packageInfo['helper'])) {
  43507. $leads = isset($this->_packageInfo['helper'][0]) ?
  43508. $this->_packageInfo['helper'] :
  43509. array($this->_packageInfo['helper']);
  43510. foreach ($leads as $maintainer) {
  43511. $s = $maintainer;
  43512. $s['handle'] = $s['user'];
  43513. unset($s['user']);
  43514. $s['role'] = 'helper';
  43515. $ret[] = $s;
  43516. }
  43517. }
  43518. return $ret;
  43519. }
  43520. return false;
  43521. }
  43522. function getLeads()
  43523. {
  43524. if (isset($this->_packageInfo['lead'])) {
  43525. return $this->_packageInfo['lead'];
  43526. }
  43527. return false;
  43528. }
  43529. function getDevelopers()
  43530. {
  43531. if (isset($this->_packageInfo['developer'])) {
  43532. return $this->_packageInfo['developer'];
  43533. }
  43534. return false;
  43535. }
  43536. function getContributors()
  43537. {
  43538. if (isset($this->_packageInfo['contributor'])) {
  43539. return $this->_packageInfo['contributor'];
  43540. }
  43541. return false;
  43542. }
  43543. function getHelpers()
  43544. {
  43545. if (isset($this->_packageInfo['helper'])) {
  43546. return $this->_packageInfo['helper'];
  43547. }
  43548. return false;
  43549. }
  43550. function setDate($date)
  43551. {
  43552. if (!isset($this->_packageInfo['date'])) {
  43553. // ensure that the extends tag is set up in the right location
  43554. $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
  43555. array('time', 'version',
  43556. 'stability', 'license', 'notes', 'contents', 'compatible',
  43557. 'dependencies', 'providesextension', 'srcpackage', 'srcuri',
  43558. 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
  43559. 'zendextbinrelease', 'bundle', 'changelog'), array(), 'date');
  43560. }
  43561. $this->_packageInfo['date'] = $date;
  43562. $this->_isValid = 0;
  43563. }
  43564. function setTime($time)
  43565. {
  43566. $this->_isValid = 0;
  43567. if (!isset($this->_packageInfo['time'])) {
  43568. // ensure that the time tag is set up in the right location
  43569. $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
  43570. array('version',
  43571. 'stability', 'license', 'notes', 'contents', 'compatible',
  43572. 'dependencies', 'providesextension', 'srcpackage', 'srcuri',
  43573. 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
  43574. 'zendextbinrelease', 'bundle', 'changelog'), $time, 'time');
  43575. }
  43576. $this->_packageInfo['time'] = $time;
  43577. }
  43578. function getDate()
  43579. {
  43580. if (isset($this->_packageInfo['date'])) {
  43581. return $this->_packageInfo['date'];
  43582. }
  43583. return false;
  43584. }
  43585. function getTime()
  43586. {
  43587. if (isset($this->_packageInfo['time'])) {
  43588. return $this->_packageInfo['time'];
  43589. }
  43590. return false;
  43591. }
  43592. /**
  43593. * @param package|api version category to return
  43594. */
  43595. function getVersion($key = 'release')
  43596. {
  43597. if (isset($this->_packageInfo['version'][$key])) {
  43598. return $this->_packageInfo['version'][$key];
  43599. }
  43600. return false;
  43601. }
  43602. function getStability()
  43603. {
  43604. if (isset($this->_packageInfo['stability'])) {
  43605. return $this->_packageInfo['stability'];
  43606. }
  43607. return false;
  43608. }
  43609. function getState($key = 'release')
  43610. {
  43611. if (isset($this->_packageInfo['stability'][$key])) {
  43612. return $this->_packageInfo['stability'][$key];
  43613. }
  43614. return false;
  43615. }
  43616. function getLicense($raw = false)
  43617. {
  43618. if (isset($this->_packageInfo['license'])) {
  43619. if ($raw) {
  43620. return $this->_packageInfo['license'];
  43621. }
  43622. if (is_array($this->_packageInfo['license'])) {
  43623. return $this->_packageInfo['license']['_content'];
  43624. } else {
  43625. return $this->_packageInfo['license'];
  43626. }
  43627. }
  43628. return false;
  43629. }
  43630. function getLicenseLocation()
  43631. {
  43632. if (!isset($this->_packageInfo['license']) || !is_array($this->_packageInfo['license'])) {
  43633. return false;
  43634. }
  43635. return $this->_packageInfo['license']['attribs'];
  43636. }
  43637. function getNotes()
  43638. {
  43639. if (isset($this->_packageInfo['notes'])) {
  43640. return $this->_packageInfo['notes'];
  43641. }
  43642. return false;
  43643. }
  43644. /**
  43645. * Return the <usesrole> tag contents, if any
  43646. * @return array|false
  43647. */
  43648. function getUsesrole()
  43649. {
  43650. if (isset($this->_packageInfo['usesrole'])) {
  43651. return $this->_packageInfo['usesrole'];
  43652. }
  43653. return false;
  43654. }
  43655. /**
  43656. * Return the <usestask> tag contents, if any
  43657. * @return array|false
  43658. */
  43659. function getUsestask()
  43660. {
  43661. if (isset($this->_packageInfo['usestask'])) {
  43662. return $this->_packageInfo['usestask'];
  43663. }
  43664. return false;
  43665. }
  43666. /**
  43667. * This should only be used to retrieve filenames and install attributes
  43668. */
  43669. function getFilelist($preserve = false)
  43670. {
  43671. if (isset($this->_packageInfo['filelist']) && !$preserve) {
  43672. return $this->_packageInfo['filelist'];
  43673. }
  43674. $this->flattenFilelist();
  43675. if ($contents = $this->getContents()) {
  43676. $ret = array();
  43677. if (!isset($contents['dir'])) {
  43678. return false;
  43679. }
  43680. if (!isset($contents['dir']['file'][0])) {
  43681. $contents['dir']['file'] = array($contents['dir']['file']);
  43682. }
  43683. foreach ($contents['dir']['file'] as $file) {
  43684. if (!isset($file['attribs']['name'])) {
  43685. continue;
  43686. }
  43687. $name = $file['attribs']['name'];
  43688. if (!$preserve) {
  43689. $file = $file['attribs'];
  43690. }
  43691. $ret[$name] = $file;
  43692. }
  43693. if (!$preserve) {
  43694. $this->_packageInfo['filelist'] = $ret;
  43695. }
  43696. return $ret;
  43697. }
  43698. return false;
  43699. }
  43700. /**
  43701. * Return configure options array, if any
  43702. *
  43703. * @return array|false
  43704. */
  43705. function getConfigureOptions()
  43706. {
  43707. if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
  43708. return false;
  43709. }
  43710. $releases = $this->getReleases();
  43711. if (isset($releases[0])) {
  43712. $releases = $releases[0];
  43713. }
  43714. if (isset($releases['configureoption'])) {
  43715. if (!isset($releases['configureoption'][0])) {
  43716. $releases['configureoption'] = array($releases['configureoption']);
  43717. }
  43718. for ($i = 0; $i < count($releases['configureoption']); $i++) {
  43719. $releases['configureoption'][$i] = $releases['configureoption'][$i]['attribs'];
  43720. }
  43721. return $releases['configureoption'];
  43722. }
  43723. return false;
  43724. }
  43725. /**
  43726. * This is only used at install-time, after all serialization
  43727. * is over.
  43728. */
  43729. function resetFilelist()
  43730. {
  43731. $this->_packageInfo['filelist'] = array();
  43732. }
  43733. /**
  43734. * Retrieve a list of files that should be installed on this computer
  43735. * @return array
  43736. */
  43737. function getInstallationFilelist($forfilecheck = false)
  43738. {
  43739. $contents = $this->getFilelist(true);
  43740. if (isset($contents['dir']['attribs']['baseinstalldir'])) {
  43741. $base = $contents['dir']['attribs']['baseinstalldir'];
  43742. }
  43743. if (isset($this->_packageInfo['bundle'])) {
  43744. return PEAR::raiseError(
  43745. 'Exception: bundles should be handled in download code only');
  43746. }
  43747. $release = $this->getReleases();
  43748. if ($release) {
  43749. if (!isset($release[0])) {
  43750. if (!isset($release['installconditions']) && !isset($release['filelist'])) {
  43751. if ($forfilecheck) {
  43752. return $this->getFilelist();
  43753. }
  43754. return $contents;
  43755. }
  43756. $release = array($release);
  43757. }
  43758. $depchecker = &$this->getPEARDependency2($this->_config, array(),
  43759. array('channel' => $this->getChannel(), 'package' => $this->getPackage()),
  43760. PEAR_VALIDATE_INSTALLING);
  43761. foreach ($release as $instance) {
  43762. if (isset($instance['installconditions'])) {
  43763. $installconditions = $instance['installconditions'];
  43764. if (is_array($installconditions)) {
  43765. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  43766. foreach ($installconditions as $type => $conditions) {
  43767. if (!isset($conditions[0])) {
  43768. $conditions = array($conditions);
  43769. }
  43770. foreach ($conditions as $condition) {
  43771. $ret = $depchecker->{"validate{$type}Dependency"}($condition);
  43772. if (PEAR::isError($ret)) {
  43773. PEAR::popErrorHandling();
  43774. continue 3; // skip this release
  43775. }
  43776. }
  43777. }
  43778. PEAR::popErrorHandling();
  43779. }
  43780. }
  43781. // this is the release to use
  43782. if (isset($instance['filelist'])) {
  43783. // ignore files
  43784. if (isset($instance['filelist']['ignore'])) {
  43785. $ignore = isset($instance['filelist']['ignore'][0]) ?
  43786. $instance['filelist']['ignore'] :
  43787. array($instance['filelist']['ignore']);
  43788. foreach ($ignore as $ig) {
  43789. unset ($contents[$ig['attribs']['name']]);
  43790. }
  43791. }
  43792. // install files as this name
  43793. if (isset($instance['filelist']['install'])) {
  43794. $installas = isset($instance['filelist']['install'][0]) ?
  43795. $instance['filelist']['install'] :
  43796. array($instance['filelist']['install']);
  43797. foreach ($installas as $as) {
  43798. $contents[$as['attribs']['name']]['attribs']['install-as'] =
  43799. $as['attribs']['as'];
  43800. }
  43801. }
  43802. }
  43803. if ($forfilecheck) {
  43804. foreach ($contents as $file => $attrs) {
  43805. $contents[$file] = $attrs['attribs'];
  43806. }
  43807. }
  43808. return $contents;
  43809. }
  43810. } else { // simple release - no installconditions or install-as
  43811. if ($forfilecheck) {
  43812. return $this->getFilelist();
  43813. }
  43814. return $contents;
  43815. }
  43816. // no releases matched
  43817. return PEAR::raiseError('No releases in package.xml matched the existing operating ' .
  43818. 'system, extensions installed, or architecture, cannot install');
  43819. }
  43820. /**
  43821. * This is only used at install-time, after all serialization
  43822. * is over.
  43823. * @param string file name
  43824. * @param string installed path
  43825. */
  43826. function setInstalledAs($file, $path)
  43827. {
  43828. if ($path) {
  43829. return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
  43830. }
  43831. unset($this->_packageInfo['filelist'][$file]['installed_as']);
  43832. }
  43833. function getInstalledLocation($file)
  43834. {
  43835. if (isset($this->_packageInfo['filelist'][$file]['installed_as'])) {
  43836. return $this->_packageInfo['filelist'][$file]['installed_as'];
  43837. }
  43838. return false;
  43839. }
  43840. /**
  43841. * This is only used at install-time, after all serialization
  43842. * is over.
  43843. */
  43844. function installedFile($file, $atts)
  43845. {
  43846. if (isset($this->_packageInfo['filelist'][$file])) {
  43847. $this->_packageInfo['filelist'][$file] =
  43848. array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']);
  43849. } else {
  43850. $this->_packageInfo['filelist'][$file] = $atts['attribs'];
  43851. }
  43852. }
  43853. /**
  43854. * Retrieve the contents tag
  43855. */
  43856. function getContents()
  43857. {
  43858. if (isset($this->_packageInfo['contents'])) {
  43859. return $this->_packageInfo['contents'];
  43860. }
  43861. return false;
  43862. }
  43863. /**
  43864. * @param string full path to file
  43865. * @param string attribute name
  43866. * @param string attribute value
  43867. * @param int risky but fast - use this to choose a file based on its position in the list
  43868. * of files. Index is zero-based like PHP arrays.
  43869. * @return bool success of operation
  43870. */
  43871. function setFileAttribute($filename, $attr, $value, $index = false)
  43872. {
  43873. $this->_isValid = 0;
  43874. if (in_array($attr, array('role', 'name', 'baseinstalldir'))) {
  43875. $this->_filesValid = false;
  43876. }
  43877. if ($index !== false &&
  43878. isset($this->_packageInfo['contents']['dir']['file'][$index]['attribs'])) {
  43879. $this->_packageInfo['contents']['dir']['file'][$index]['attribs'][$attr] = $value;
  43880. return true;
  43881. }
  43882. if (!isset($this->_packageInfo['contents']['dir']['file'])) {
  43883. return false;
  43884. }
  43885. $files = $this->_packageInfo['contents']['dir']['file'];
  43886. if (!isset($files[0])) {
  43887. $files = array($files);
  43888. $ind = false;
  43889. } else {
  43890. $ind = true;
  43891. }
  43892. foreach ($files as $i => $file) {
  43893. if (isset($file['attribs'])) {
  43894. if ($file['attribs']['name'] == $filename) {
  43895. if ($ind) {
  43896. $this->_packageInfo['contents']['dir']['file'][$i]['attribs'][$attr] = $value;
  43897. } else {
  43898. $this->_packageInfo['contents']['dir']['file']['attribs'][$attr] = $value;
  43899. }
  43900. return true;
  43901. }
  43902. }
  43903. }
  43904. return false;
  43905. }
  43906. function setDirtree($path)
  43907. {
  43908. if (!isset($this->_packageInfo['dirtree'])) {
  43909. $this->_packageInfo['dirtree'] = array();
  43910. }
  43911. $this->_packageInfo['dirtree'][$path] = true;
  43912. }
  43913. function getDirtree()
  43914. {
  43915. if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
  43916. return $this->_packageInfo['dirtree'];
  43917. }
  43918. return false;
  43919. }
  43920. function resetDirtree()
  43921. {
  43922. unset($this->_packageInfo['dirtree']);
  43923. }
  43924. /**
  43925. * Determines whether this package claims it is compatible with the version of
  43926. * the package that has a recommended version dependency
  43927. * @param PEAR_PackageFile_v2|PEAR_PackageFile_v1|PEAR_Downloader_Package
  43928. * @return boolean
  43929. */
  43930. function isCompatible($pf)
  43931. {
  43932. if (!isset($this->_packageInfo['compatible'])) {
  43933. return false;
  43934. }
  43935. if (!isset($this->_packageInfo['channel'])) {
  43936. return false;
  43937. }
  43938. $me = $pf->getVersion();
  43939. $compatible = $this->_packageInfo['compatible'];
  43940. if (!isset($compatible[0])) {
  43941. $compatible = array($compatible);
  43942. }
  43943. $found = false;
  43944. foreach ($compatible as $info) {
  43945. if (strtolower($info['name']) == strtolower($pf->getPackage())) {
  43946. if (strtolower($info['channel']) == strtolower($pf->getChannel())) {
  43947. $found = true;
  43948. break;
  43949. }
  43950. }
  43951. }
  43952. if (!$found) {
  43953. return false;
  43954. }
  43955. if (isset($info['exclude'])) {
  43956. if (!isset($info['exclude'][0])) {
  43957. $info['exclude'] = array($info['exclude']);
  43958. }
  43959. foreach ($info['exclude'] as $exclude) {
  43960. if (version_compare($me, $exclude, '==')) {
  43961. return false;
  43962. }
  43963. }
  43964. }
  43965. if (version_compare($me, $info['min'], '>=') && version_compare($me, $info['max'], '<=')) {
  43966. return true;
  43967. }
  43968. return false;
  43969. }
  43970. /**
  43971. * @return array|false
  43972. */
  43973. function getCompatible()
  43974. {
  43975. if (isset($this->_packageInfo['compatible'])) {
  43976. return $this->_packageInfo['compatible'];
  43977. }
  43978. return false;
  43979. }
  43980. function getDependencies()
  43981. {
  43982. if (isset($this->_packageInfo['dependencies'])) {
  43983. return $this->_packageInfo['dependencies'];
  43984. }
  43985. return false;
  43986. }
  43987. function isSubpackageOf($p)
  43988. {
  43989. return $p->isSubpackage($this);
  43990. }
  43991. /**
  43992. * Determines whether the passed in package is a subpackage of this package.
  43993. *
  43994. * No version checking is done, only name verification.
  43995. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  43996. * @return bool
  43997. */
  43998. function isSubpackage($p)
  43999. {
  44000. $sub = array();
  44001. if (isset($this->_packageInfo['dependencies']['required']['subpackage'])) {
  44002. $sub = $this->_packageInfo['dependencies']['required']['subpackage'];
  44003. if (!isset($sub[0])) {
  44004. $sub = array($sub);
  44005. }
  44006. }
  44007. if (isset($this->_packageInfo['dependencies']['optional']['subpackage'])) {
  44008. $sub1 = $this->_packageInfo['dependencies']['optional']['subpackage'];
  44009. if (!isset($sub1[0])) {
  44010. $sub1 = array($sub1);
  44011. }
  44012. $sub = array_merge($sub, $sub1);
  44013. }
  44014. if (isset($this->_packageInfo['dependencies']['group'])) {
  44015. $group = $this->_packageInfo['dependencies']['group'];
  44016. if (!isset($group[0])) {
  44017. $group = array($group);
  44018. }
  44019. foreach ($group as $deps) {
  44020. if (isset($deps['subpackage'])) {
  44021. $sub2 = $deps['subpackage'];
  44022. if (!isset($sub2[0])) {
  44023. $sub2 = array($sub2);
  44024. }
  44025. $sub = array_merge($sub, $sub2);
  44026. }
  44027. }
  44028. }
  44029. foreach ($sub as $dep) {
  44030. if (strtolower($dep['name']) == strtolower($p->getPackage())) {
  44031. if (isset($dep['channel'])) {
  44032. if (strtolower($dep['channel']) == strtolower($p->getChannel())) {
  44033. return true;
  44034. }
  44035. } else {
  44036. if ($dep['uri'] == $p->getURI()) {
  44037. return true;
  44038. }
  44039. }
  44040. }
  44041. }
  44042. return false;
  44043. }
  44044. function dependsOn($package, $channel)
  44045. {
  44046. if (!($deps = $this->getDependencies())) {
  44047. return false;
  44048. }
  44049. foreach (array('package', 'subpackage') as $type) {
  44050. foreach (array('required', 'optional') as $needed) {
  44051. if (isset($deps[$needed][$type])) {
  44052. if (!isset($deps[$needed][$type][0])) {
  44053. $deps[$needed][$type] = array($deps[$needed][$type]);
  44054. }
  44055. foreach ($deps[$needed][$type] as $dep) {
  44056. $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
  44057. if (strtolower($dep['name']) == strtolower($package) &&
  44058. $depchannel == $channel) {
  44059. return true;
  44060. }
  44061. }
  44062. }
  44063. }
  44064. if (isset($deps['group'])) {
  44065. if (!isset($deps['group'][0])) {
  44066. $dep['group'] = array($deps['group']);
  44067. }
  44068. foreach ($deps['group'] as $group) {
  44069. if (isset($group[$type])) {
  44070. if (!is_array($group[$type])) {
  44071. $group[$type] = array($group[$type]);
  44072. }
  44073. foreach ($group[$type] as $dep) {
  44074. $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
  44075. if (strtolower($dep['name']) == strtolower($package) &&
  44076. $depchannel == $channel) {
  44077. return true;
  44078. }
  44079. }
  44080. }
  44081. }
  44082. }
  44083. }
  44084. return false;
  44085. }
  44086. /**
  44087. * Get the contents of a dependency group
  44088. * @param string
  44089. * @return array|false
  44090. */
  44091. function getDependencyGroup($name)
  44092. {
  44093. $name = strtolower($name);
  44094. if (!isset($this->_packageInfo['dependencies']['group'])) {
  44095. return false;
  44096. }
  44097. $groups = $this->_packageInfo['dependencies']['group'];
  44098. if (!isset($groups[0])) {
  44099. $groups = array($groups);
  44100. }
  44101. foreach ($groups as $group) {
  44102. if (strtolower($group['attribs']['name']) == $name) {
  44103. return $group;
  44104. }
  44105. }
  44106. return false;
  44107. }
  44108. /**
  44109. * Retrieve a partial package.xml 1.0 representation of dependencies
  44110. *
  44111. * a very limited representation of dependencies is returned by this method.
  44112. * The <exclude> tag for excluding certain versions of a dependency is
  44113. * completely ignored. In addition, dependency groups are ignored, with the
  44114. * assumption that all dependencies in dependency groups are also listed in
  44115. * the optional group that work with all dependency groups
  44116. * @param boolean return package.xml 2.0 <dependencies> tag
  44117. * @return array|false
  44118. */
  44119. function getDeps($raw = false, $nopearinstaller = false)
  44120. {
  44121. if (isset($this->_packageInfo['dependencies'])) {
  44122. if ($raw) {
  44123. return $this->_packageInfo['dependencies'];
  44124. }
  44125. $ret = array();
  44126. $map = array(
  44127. 'php' => 'php',
  44128. 'package' => 'pkg',
  44129. 'subpackage' => 'pkg',
  44130. 'extension' => 'ext',
  44131. 'os' => 'os',
  44132. 'pearinstaller' => 'pkg',
  44133. );
  44134. foreach (array('required', 'optional') as $type) {
  44135. $optional = ($type == 'optional') ? 'yes' : 'no';
  44136. if (!isset($this->_packageInfo['dependencies'][$type])
  44137. || empty($this->_packageInfo['dependencies'][$type])) {
  44138. continue;
  44139. }
  44140. foreach ($this->_packageInfo['dependencies'][$type] as $dtype => $deps) {
  44141. if ($dtype == 'pearinstaller' && $nopearinstaller) {
  44142. continue;
  44143. }
  44144. if ((is_array($deps) && !isset($deps[0])) || !is_array($deps)) {
  44145. $deps = array($deps);
  44146. }
  44147. foreach ($deps as $dep) {
  44148. if (!isset($map[$dtype])) {
  44149. // no support for arch type
  44150. continue;
  44151. }
  44152. if ($dtype == 'pearinstaller') {
  44153. $dep['name'] = 'PEAR';
  44154. $dep['channel'] = 'pear.php.net';
  44155. }
  44156. $s = array('type' => $map[$dtype]);
  44157. if (isset($dep['channel'])) {
  44158. $s['channel'] = $dep['channel'];
  44159. }
  44160. if (isset($dep['uri'])) {
  44161. $s['uri'] = $dep['uri'];
  44162. }
  44163. if (isset($dep['name'])) {
  44164. $s['name'] = $dep['name'];
  44165. }
  44166. if (isset($dep['conflicts'])) {
  44167. $s['rel'] = 'not';
  44168. } else {
  44169. if (!isset($dep['min']) &&
  44170. !isset($dep['max'])) {
  44171. $s['rel'] = 'has';
  44172. $s['optional'] = $optional;
  44173. } elseif (isset($dep['min']) &&
  44174. isset($dep['max'])) {
  44175. $s['rel'] = 'ge';
  44176. $s1 = $s;
  44177. $s1['rel'] = 'le';
  44178. $s['version'] = $dep['min'];
  44179. $s1['version'] = $dep['max'];
  44180. if (isset($dep['channel'])) {
  44181. $s1['channel'] = $dep['channel'];
  44182. }
  44183. if ($dtype != 'php') {
  44184. $s['name'] = $dep['name'];
  44185. $s1['name'] = $dep['name'];
  44186. }
  44187. $s['optional'] = $optional;
  44188. $s1['optional'] = $optional;
  44189. $ret[] = $s1;
  44190. } elseif (isset($dep['min'])) {
  44191. if (isset($dep['exclude']) &&
  44192. $dep['exclude'] == $dep['min']) {
  44193. $s['rel'] = 'gt';
  44194. } else {
  44195. $s['rel'] = 'ge';
  44196. }
  44197. $s['version'] = $dep['min'];
  44198. $s['optional'] = $optional;
  44199. if ($dtype != 'php') {
  44200. $s['name'] = $dep['name'];
  44201. }
  44202. } elseif (isset($dep['max'])) {
  44203. if (isset($dep['exclude']) &&
  44204. $dep['exclude'] == $dep['max']) {
  44205. $s['rel'] = 'lt';
  44206. } else {
  44207. $s['rel'] = 'le';
  44208. }
  44209. $s['version'] = $dep['max'];
  44210. $s['optional'] = $optional;
  44211. if ($dtype != 'php') {
  44212. $s['name'] = $dep['name'];
  44213. }
  44214. }
  44215. }
  44216. $ret[] = $s;
  44217. }
  44218. }
  44219. }
  44220. if (count($ret)) {
  44221. return $ret;
  44222. }
  44223. }
  44224. return false;
  44225. }
  44226. /**
  44227. * @return php|extsrc|extbin|zendextsrc|zendextbin|bundle|false
  44228. */
  44229. function getPackageType()
  44230. {
  44231. if (isset($this->_packageInfo['phprelease'])) {
  44232. return 'php';
  44233. }
  44234. if (isset($this->_packageInfo['extsrcrelease'])) {
  44235. return 'extsrc';
  44236. }
  44237. if (isset($this->_packageInfo['extbinrelease'])) {
  44238. return 'extbin';
  44239. }
  44240. if (isset($this->_packageInfo['zendextsrcrelease'])) {
  44241. return 'zendextsrc';
  44242. }
  44243. if (isset($this->_packageInfo['zendextbinrelease'])) {
  44244. return 'zendextbin';
  44245. }
  44246. if (isset($this->_packageInfo['bundle'])) {
  44247. return 'bundle';
  44248. }
  44249. return false;
  44250. }
  44251. /**
  44252. * @return array|false
  44253. */
  44254. function getReleases()
  44255. {
  44256. $type = $this->getPackageType();
  44257. if ($type != 'bundle') {
  44258. $type .= 'release';
  44259. }
  44260. if ($this->getPackageType() && isset($this->_packageInfo[$type])) {
  44261. return $this->_packageInfo[$type];
  44262. }
  44263. return false;
  44264. }
  44265. /**
  44266. * @return array
  44267. */
  44268. function getChangelog()
  44269. {
  44270. if (isset($this->_packageInfo['changelog'])) {
  44271. return $this->_packageInfo['changelog'];
  44272. }
  44273. return false;
  44274. }
  44275. function hasDeps()
  44276. {
  44277. return isset($this->_packageInfo['dependencies']);
  44278. }
  44279. function getPackagexmlVersion()
  44280. {
  44281. if (isset($this->_packageInfo['zendextsrcrelease'])) {
  44282. return '2.1';
  44283. }
  44284. if (isset($this->_packageInfo['zendextbinrelease'])) {
  44285. return '2.1';
  44286. }
  44287. return '2.0';
  44288. }
  44289. /**
  44290. * @return array|false
  44291. */
  44292. function getSourcePackage()
  44293. {
  44294. if (isset($this->_packageInfo['extbinrelease']) ||
  44295. isset($this->_packageInfo['zendextbinrelease'])) {
  44296. return array('channel' => $this->_packageInfo['srcchannel'],
  44297. 'package' => $this->_packageInfo['srcpackage']);
  44298. }
  44299. return false;
  44300. }
  44301. function getBundledPackages()
  44302. {
  44303. if (isset($this->_packageInfo['bundle'])) {
  44304. return $this->_packageInfo['contents']['bundledpackage'];
  44305. }
  44306. return false;
  44307. }
  44308. function getLastModified()
  44309. {
  44310. if (isset($this->_packageInfo['_lastmodified'])) {
  44311. return $this->_packageInfo['_lastmodified'];
  44312. }
  44313. return false;
  44314. }
  44315. /**
  44316. * Get the contents of a file listed within the package.xml
  44317. * @param string
  44318. * @return string
  44319. */
  44320. function getFileContents($file)
  44321. {
  44322. if ($this->_archiveFile == $this->_packageFile) { // unpacked
  44323. $dir = dirname($this->_packageFile);
  44324. $file = $dir . DIRECTORY_SEPARATOR . $file;
  44325. $file = str_replace(array('/', '\\'),
  44326. array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
  44327. if (file_exists($file) && is_readable($file)) {
  44328. return implode('', file($file));
  44329. }
  44330. } else { // tgz
  44331. $tar = new Archive_Tar($this->_archiveFile);
  44332. $tar->pushErrorHandling(PEAR_ERROR_RETURN);
  44333. if ($file != 'package.xml' && $file != 'package2.xml') {
  44334. $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
  44335. }
  44336. $file = $tar->extractInString($file);
  44337. $tar->popErrorHandling();
  44338. if (PEAR::isError($file)) {
  44339. return PEAR::raiseError("Cannot locate file '$file' in archive");
  44340. }
  44341. return $file;
  44342. }
  44343. }
  44344. function &getRW()
  44345. {
  44346. if (!class_exists('PEAR_PackageFile_v2_rw')) {
  44347. require_once 'PEAR/PackageFile/v2/rw.php';
  44348. }
  44349. $a = new PEAR_PackageFile_v2_rw;
  44350. foreach (get_object_vars($this) as $name => $unused) {
  44351. if (!isset($this->$name)) {
  44352. continue;
  44353. }
  44354. if ($name == '_config' || $name == '_logger'|| $name == '_registry' ||
  44355. $name == '_stack') {
  44356. $a->$name = &$this->$name;
  44357. } else {
  44358. $a->$name = $this->$name;
  44359. }
  44360. }
  44361. return $a;
  44362. }
  44363. function &getDefaultGenerator()
  44364. {
  44365. if (!class_exists('PEAR_PackageFile_Generator_v2')) {
  44366. require_once 'PEAR/PackageFile/Generator/v2.php';
  44367. }
  44368. $a = new PEAR_PackageFile_Generator_v2($this);
  44369. return $a;
  44370. }
  44371. function analyzeSourceCode($file, $string = false)
  44372. {
  44373. if (!isset($this->_v2Validator) ||
  44374. !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
  44375. if (!class_exists('PEAR_PackageFile_v2_Validator')) {
  44376. require_once 'PEAR/PackageFile/v2/Validator.php';
  44377. }
  44378. $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
  44379. }
  44380. return $this->_v2Validator->analyzeSourceCode($file, $string);
  44381. }
  44382. function validate($state = PEAR_VALIDATE_NORMAL)
  44383. {
  44384. if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
  44385. return false;
  44386. }
  44387. if (!isset($this->_v2Validator) ||
  44388. !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
  44389. if (!class_exists('PEAR_PackageFile_v2_Validator')) {
  44390. require_once 'PEAR/PackageFile/v2/Validator.php';
  44391. }
  44392. $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
  44393. }
  44394. if (isset($this->_packageInfo['xsdversion'])) {
  44395. unset($this->_packageInfo['xsdversion']);
  44396. }
  44397. return $this->_v2Validator->validate($this, $state);
  44398. }
  44399. function getTasksNs()
  44400. {
  44401. if (!isset($this->_tasksNs)) {
  44402. if (isset($this->_packageInfo['attribs'])) {
  44403. foreach ($this->_packageInfo['attribs'] as $name => $value) {
  44404. if ($value == 'http://pear.php.net/dtd/tasks-1.0') {
  44405. $this->_tasksNs = str_replace('xmlns:', '', $name);
  44406. break;
  44407. }
  44408. }
  44409. }
  44410. }
  44411. return $this->_tasksNs;
  44412. }
  44413. /**
  44414. * Determine whether a task name is a valid task. Custom tasks may be defined
  44415. * using subdirectories by putting a "-" in the name, as in <tasks:mycustom-task>
  44416. *
  44417. * Note that this method will auto-load the task class file and test for the existence
  44418. * of the name with "-" replaced by "_" as in PEAR/Task/mycustom/task.php makes class
  44419. * PEAR_Task_mycustom_task
  44420. * @param string
  44421. * @return boolean
  44422. */
  44423. function getTask($task)
  44424. {
  44425. $this->getTasksNs();
  44426. // transform all '-' to '/' and 'tasks:' to '' so tasks:replace becomes replace
  44427. $task = str_replace(array($this->_tasksNs . ':', '-'), array('', ' '), $task);
  44428. $taskfile = str_replace(' ', '/', ucwords($task));
  44429. $task = str_replace(array(' ', '/'), '_', ucwords($task));
  44430. if (class_exists("PEAR_Task_$task")) {
  44431. return "PEAR_Task_$task";
  44432. }
  44433. $fp = @fopen("PEAR/Task/$taskfile.php", 'r', true);
  44434. if ($fp) {
  44435. fclose($fp);
  44436. require_once "PEAR/Task/$taskfile.php";
  44437. return "PEAR_Task_$task";
  44438. }
  44439. return false;
  44440. }
  44441. /**
  44442. * Key-friendly array_splice
  44443. * @param tagname to splice a value in before
  44444. * @param mixed the value to splice in
  44445. * @param string the new tag name
  44446. */
  44447. function _ksplice($array, $key, $value, $newkey)
  44448. {
  44449. $offset = array_search($key, array_keys($array));
  44450. $after = array_slice($array, $offset);
  44451. $before = array_slice($array, 0, $offset);
  44452. $before[$newkey] = $value;
  44453. return array_merge($before, $after);
  44454. }
  44455. /**
  44456. * @param array a list of possible keys, in the order they may occur
  44457. * @param mixed contents of the new package.xml tag
  44458. * @param string tag name
  44459. * @access private
  44460. */
  44461. function _insertBefore($array, $keys, $contents, $newkey)
  44462. {
  44463. foreach ($keys as $key) {
  44464. if (isset($array[$key])) {
  44465. return $array = $this->_ksplice($array, $key, $contents, $newkey);
  44466. }
  44467. }
  44468. $array[$newkey] = $contents;
  44469. return $array;
  44470. }
  44471. /**
  44472. * @param subsection of {@link $_packageInfo}
  44473. * @param array|string tag contents
  44474. * @param array format:
  44475. * <pre>
  44476. * array(
  44477. * tagname => array(list of tag names that follow this one),
  44478. * childtagname => array(list of child tag names that follow this one),
  44479. * )
  44480. * </pre>
  44481. *
  44482. * This allows construction of nested tags
  44483. * @access private
  44484. */
  44485. function _mergeTag($manip, $contents, $order)
  44486. {
  44487. if (count($order)) {
  44488. foreach ($order as $tag => $curorder) {
  44489. if (!isset($manip[$tag])) {
  44490. // ensure that the tag is set up
  44491. $manip = $this->_insertBefore($manip, $curorder, array(), $tag);
  44492. }
  44493. if (count($order) > 1) {
  44494. $manip[$tag] = $this->_mergeTag($manip[$tag], $contents, array_slice($order, 1));
  44495. return $manip;
  44496. }
  44497. }
  44498. } else {
  44499. return $manip;
  44500. }
  44501. if (is_array($manip[$tag]) && !empty($manip[$tag]) && isset($manip[$tag][0])) {
  44502. $manip[$tag][] = $contents;
  44503. } else {
  44504. if (is_array($manip[$tag]) && !count($manip[$tag])) {
  44505. $manip[$tag] = $contents;
  44506. } else {
  44507. $manip[$tag] = array($manip[$tag]);
  44508. $manip[$tag][] = $contents;
  44509. }
  44510. }
  44511. return $manip;
  44512. }
  44513. }
  44514. ?>
  44515. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/REST/10.php�����������������������������������������������������������������������0000644�0001750�0001750�00000077621�14720722517�014540� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  44516. /**
  44517. * PEAR_REST_10
  44518. *
  44519. * PHP versions 4 and 5
  44520. *
  44521. * @category pear
  44522. * @package PEAR
  44523. * @author Greg Beaver <cellog@php.net>
  44524. * @copyright 1997-2009 The Authors
  44525. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  44526. * @link http://pear.php.net/package/PEAR
  44527. * @since File available since Release 1.4.0a12
  44528. */
  44529. /**
  44530. * For downloading REST xml/txt files
  44531. */
  44532. require_once 'PEAR/REST.php';
  44533. /**
  44534. * Implement REST 1.0
  44535. *
  44536. * @category pear
  44537. * @package PEAR
  44538. * @author Greg Beaver <cellog@php.net>
  44539. * @copyright 1997-2009 The Authors
  44540. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  44541. * @version Release: 1.10.16
  44542. * @link http://pear.php.net/package/PEAR
  44543. * @since Class available since Release 1.4.0a12
  44544. */
  44545. class PEAR_REST_10
  44546. {
  44547. /**
  44548. * @var PEAR_REST
  44549. */
  44550. var $_rest;
  44551. function __construct($config, $options = array())
  44552. {
  44553. $this->_rest = new PEAR_REST($config, $options);
  44554. }
  44555. /**
  44556. * Retrieve information about a remote package to be downloaded from a REST server
  44557. *
  44558. * @param string $base The uri to prepend to all REST calls
  44559. * @param array $packageinfo an array of format:
  44560. * <pre>
  44561. * array(
  44562. * 'package' => 'packagename',
  44563. * 'channel' => 'channelname',
  44564. * ['state' => 'alpha' (or valid state),]
  44565. * -or-
  44566. * ['version' => '1.whatever']
  44567. * </pre>
  44568. * @param string $prefstate Current preferred_state config variable value
  44569. * @param bool $installed the installed version of this package to compare against
  44570. * @return array|false|PEAR_Error see {@link _returnDownloadURL()}
  44571. */
  44572. function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false)
  44573. {
  44574. $states = $this->betterStates($prefstate, true);
  44575. if (!$states) {
  44576. return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
  44577. }
  44578. $channel = $packageinfo['channel'];
  44579. $package = $packageinfo['package'];
  44580. $state = isset($packageinfo['state']) ? $packageinfo['state'] : null;
  44581. $version = isset($packageinfo['version']) ? $packageinfo['version'] : null;
  44582. $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml';
  44583. $info = $this->_rest->retrieveData($restFile, false, false, $channel);
  44584. if (PEAR::isError($info)) {
  44585. return PEAR::raiseError('No releases available for package "' .
  44586. $channel . '/' . $package . '"');
  44587. }
  44588. if (!isset($info['r'])) {
  44589. return false;
  44590. }
  44591. $release = $found = false;
  44592. if (!is_array($info['r']) || !isset($info['r'][0])) {
  44593. $info['r'] = array($info['r']);
  44594. }
  44595. foreach ($info['r'] as $release) {
  44596. if (!isset($this->_rest->_options['force']) && ($installed &&
  44597. version_compare($release['v'], $installed, '<'))) {
  44598. continue;
  44599. }
  44600. if (isset($state)) {
  44601. // try our preferred state first
  44602. if ($release['s'] == $state) {
  44603. $found = true;
  44604. break;
  44605. }
  44606. // see if there is something newer and more stable
  44607. // bug #7221
  44608. if (in_array($release['s'], $this->betterStates($state), true)) {
  44609. $found = true;
  44610. break;
  44611. }
  44612. } elseif (isset($version)) {
  44613. if ($release['v'] == $version) {
  44614. $found = true;
  44615. break;
  44616. }
  44617. } else {
  44618. if (in_array($release['s'], $states)) {
  44619. $found = true;
  44620. break;
  44621. }
  44622. }
  44623. }
  44624. return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel);
  44625. }
  44626. function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage,
  44627. $prefstate = 'stable', $installed = false, $channel = false)
  44628. {
  44629. $states = $this->betterStates($prefstate, true);
  44630. if (!$states) {
  44631. return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
  44632. }
  44633. $channel = $dependency['channel'];
  44634. $package = $dependency['name'];
  44635. $state = isset($dependency['state']) ? $dependency['state'] : null;
  44636. $version = isset($dependency['version']) ? $dependency['version'] : null;
  44637. $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml';
  44638. $info = $this->_rest->retrieveData($restFile, false, false, $channel);
  44639. if (PEAR::isError($info)) {
  44640. return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package']
  44641. . '" dependency "' . $channel . '/' . $package . '" has no releases');
  44642. }
  44643. if (!is_array($info) || !isset($info['r'])) {
  44644. return false;
  44645. }
  44646. $exclude = array();
  44647. $min = $max = $recommended = false;
  44648. if ($xsdversion == '1.0') {
  44649. switch ($dependency['rel']) {
  44650. case 'ge' :
  44651. $min = $dependency['version'];
  44652. break;
  44653. case 'gt' :
  44654. $min = $dependency['version'];
  44655. $exclude = array($dependency['version']);
  44656. break;
  44657. case 'eq' :
  44658. $recommended = $dependency['version'];
  44659. break;
  44660. case 'lt' :
  44661. $max = $dependency['version'];
  44662. $exclude = array($dependency['version']);
  44663. break;
  44664. case 'le' :
  44665. $max = $dependency['version'];
  44666. break;
  44667. case 'ne' :
  44668. $exclude = array($dependency['version']);
  44669. break;
  44670. }
  44671. } else {
  44672. $min = isset($dependency['min']) ? $dependency['min'] : false;
  44673. $max = isset($dependency['max']) ? $dependency['max'] : false;
  44674. $recommended = isset($dependency['recommended']) ?
  44675. $dependency['recommended'] : false;
  44676. if (isset($dependency['exclude'])) {
  44677. if (!isset($dependency['exclude'][0])) {
  44678. $exclude = array($dependency['exclude']);
  44679. }
  44680. }
  44681. }
  44682. $release = $found = false;
  44683. if (!is_array($info['r']) || !isset($info['r'][0])) {
  44684. $info['r'] = array($info['r']);
  44685. }
  44686. foreach ($info['r'] as $release) {
  44687. if (!isset($this->_rest->_options['force']) && ($installed &&
  44688. version_compare($release['v'], $installed, '<'))) {
  44689. continue;
  44690. }
  44691. if (in_array($release['v'], $exclude)) { // skip excluded versions
  44692. continue;
  44693. }
  44694. // allow newer releases to say "I'm OK with the dependent package"
  44695. if ($xsdversion == '2.0' && isset($release['co'])) {
  44696. if (!is_array($release['co']) || !isset($release['co'][0])) {
  44697. $release['co'] = array($release['co']);
  44698. }
  44699. foreach ($release['co'] as $entry) {
  44700. if (isset($entry['x']) && !is_array($entry['x'])) {
  44701. $entry['x'] = array($entry['x']);
  44702. } elseif (!isset($entry['x'])) {
  44703. $entry['x'] = array();
  44704. }
  44705. if ($entry['c'] == $deppackage['channel'] &&
  44706. strtolower($entry['p']) == strtolower($deppackage['package']) &&
  44707. version_compare($deppackage['version'], $entry['min'], '>=') &&
  44708. version_compare($deppackage['version'], $entry['max'], '<=') &&
  44709. !in_array($release['v'], $entry['x'])) {
  44710. $recommended = $release['v'];
  44711. break;
  44712. }
  44713. }
  44714. }
  44715. if ($recommended) {
  44716. if ($release['v'] != $recommended) { // if we want a specific
  44717. // version, then skip all others
  44718. continue;
  44719. } else {
  44720. if (!in_array($release['s'], $states)) {
  44721. // the stability is too low, but we must return the
  44722. // recommended version if possible
  44723. return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel);
  44724. }
  44725. }
  44726. }
  44727. if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions
  44728. continue;
  44729. }
  44730. if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions
  44731. continue;
  44732. }
  44733. if ($installed && version_compare($release['v'], $installed, '<')) {
  44734. continue;
  44735. }
  44736. if (in_array($release['s'], $states)) { // if in the preferred state...
  44737. $found = true; // ... then use it
  44738. break;
  44739. }
  44740. }
  44741. return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel);
  44742. }
  44743. /**
  44744. * Take raw data and return the array needed for processing a download URL
  44745. *
  44746. * @param string $base REST base uri
  44747. * @param string $package Package name
  44748. * @param array $release an array of format array('v' => version, 's' => state)
  44749. * describing the release to download
  44750. * @param array $info list of all releases as defined by allreleases.xml
  44751. * @param bool|null $found determines whether the release was found or this is the next
  44752. * best alternative. If null, then versions were skipped because
  44753. * of PHP dependency
  44754. * @return array|PEAR_Error
  44755. * @access private
  44756. */
  44757. function _returnDownloadURL($base, $package, $release, $info, $found, $phpversion = false, $channel = false)
  44758. {
  44759. if (!$found) {
  44760. $release = $info['r'][0];
  44761. }
  44762. $packageLower = strtolower($package);
  44763. $pinfo = $this->_rest->retrieveCacheFirst($base . 'p/' . $packageLower . '/' .
  44764. 'info.xml', false, false, $channel);
  44765. if (PEAR::isError($pinfo)) {
  44766. return PEAR::raiseError('Package "' . $package .
  44767. '" does not have REST info xml available');
  44768. }
  44769. $releaseinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' .
  44770. $release['v'] . '.xml', false, false, $channel);
  44771. if (PEAR::isError($releaseinfo)) {
  44772. return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] .
  44773. '" does not have REST xml available');
  44774. }
  44775. $packagexml = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' .
  44776. 'deps.' . $release['v'] . '.txt', false, true, $channel);
  44777. if (PEAR::isError($packagexml)) {
  44778. return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] .
  44779. '" does not have REST dependency information available');
  44780. }
  44781. $packagexml = unserialize($packagexml);
  44782. if (!$packagexml) {
  44783. $packagexml = array();
  44784. }
  44785. $allinfo = $this->_rest->retrieveData($base . 'r/' . $packageLower .
  44786. '/allreleases.xml', false, false, $channel);
  44787. if (PEAR::isError($allinfo)) {
  44788. return $allinfo;
  44789. }
  44790. if (!is_array($allinfo['r']) || !isset($allinfo['r'][0])) {
  44791. $allinfo['r'] = array($allinfo['r']);
  44792. }
  44793. $compatible = false;
  44794. foreach ($allinfo['r'] as $release) {
  44795. if ($release['v'] != $releaseinfo['v']) {
  44796. continue;
  44797. }
  44798. if (!isset($release['co'])) {
  44799. break;
  44800. }
  44801. $compatible = array();
  44802. if (!is_array($release['co']) || !isset($release['co'][0])) {
  44803. $release['co'] = array($release['co']);
  44804. }
  44805. foreach ($release['co'] as $entry) {
  44806. $comp = array();
  44807. $comp['name'] = $entry['p'];
  44808. $comp['channel'] = $entry['c'];
  44809. $comp['min'] = $entry['min'];
  44810. $comp['max'] = $entry['max'];
  44811. if (isset($entry['x']) && !is_array($entry['x'])) {
  44812. $comp['exclude'] = $entry['x'];
  44813. }
  44814. $compatible[] = $comp;
  44815. }
  44816. if (count($compatible) == 1) {
  44817. $compatible = $compatible[0];
  44818. }
  44819. break;
  44820. }
  44821. $deprecated = false;
  44822. if (isset($pinfo['dc']) && isset($pinfo['dp'])) {
  44823. if (is_array($pinfo['dp'])) {
  44824. $deprecated = array('channel' => (string) $pinfo['dc'],
  44825. 'package' => trim($pinfo['dp']['_content']));
  44826. } else {
  44827. $deprecated = array('channel' => (string) $pinfo['dc'],
  44828. 'package' => trim($pinfo['dp']));
  44829. }
  44830. }
  44831. $return = array(
  44832. 'version' => $releaseinfo['v'],
  44833. 'info' => $packagexml,
  44834. 'package' => $releaseinfo['p']['_content'],
  44835. 'stability' => $releaseinfo['st'],
  44836. 'compatible' => $compatible,
  44837. 'deprecated' => $deprecated,
  44838. );
  44839. if ($found) {
  44840. $return['url'] = $releaseinfo['g'];
  44841. return $return;
  44842. }
  44843. $return['php'] = $phpversion;
  44844. return $return;
  44845. }
  44846. function listPackages($base, $channel = false)
  44847. {
  44848. $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
  44849. if (PEAR::isError($packagelist)) {
  44850. return $packagelist;
  44851. }
  44852. if (!is_array($packagelist) || !isset($packagelist['p'])) {
  44853. return array();
  44854. }
  44855. if (!is_array($packagelist['p'])) {
  44856. $packagelist['p'] = array($packagelist['p']);
  44857. }
  44858. return $packagelist['p'];
  44859. }
  44860. /**
  44861. * List all categories of a REST server
  44862. *
  44863. * @param string $base base URL of the server
  44864. * @return array of categorynames
  44865. */
  44866. function listCategories($base, $channel = false)
  44867. {
  44868. $categories = array();
  44869. // c/categories.xml does not exist;
  44870. // check for every package its category manually
  44871. // This is SLOOOWWWW : ///
  44872. $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
  44873. if (PEAR::isError($packagelist)) {
  44874. return $packagelist;
  44875. }
  44876. if (!is_array($packagelist) || !isset($packagelist['p'])) {
  44877. $ret = array();
  44878. return $ret;
  44879. }
  44880. if (!is_array($packagelist['p'])) {
  44881. $packagelist['p'] = array($packagelist['p']);
  44882. }
  44883. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  44884. foreach ($packagelist['p'] as $package) {
  44885. $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
  44886. if (PEAR::isError($inf)) {
  44887. PEAR::popErrorHandling();
  44888. return $inf;
  44889. }
  44890. $cat = $inf['ca']['_content'];
  44891. if (!isset($categories[$cat])) {
  44892. $categories[$cat] = $inf['ca'];
  44893. }
  44894. }
  44895. return array_values($categories);
  44896. }
  44897. /**
  44898. * List a category of a REST server
  44899. *
  44900. * @param string $base base URL of the server
  44901. * @param string $category name of the category
  44902. * @param boolean $info also download full package info
  44903. * @return array of packagenames
  44904. */
  44905. function listCategory($base, $category, $info = false, $channel = false)
  44906. {
  44907. // gives '404 Not Found' error when category doesn't exist
  44908. $packagelist = $this->_rest->retrieveData($base.'c/'.urlencode($category).'/packages.xml', false, false, $channel);
  44909. if (PEAR::isError($packagelist)) {
  44910. return $packagelist;
  44911. }
  44912. if (!is_array($packagelist) || !isset($packagelist['p'])) {
  44913. return array();
  44914. }
  44915. if (!is_array($packagelist['p']) ||
  44916. !isset($packagelist['p'][0])) { // only 1 pkg
  44917. $packagelist = array($packagelist['p']);
  44918. } else {
  44919. $packagelist = $packagelist['p'];
  44920. }
  44921. if ($info == true) {
  44922. // get individual package info
  44923. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  44924. foreach ($packagelist as $i => $packageitem) {
  44925. $url = sprintf('%s'.'r/%s/latest.txt',
  44926. $base,
  44927. strtolower($packageitem['_content']));
  44928. $version = $this->_rest->retrieveData($url, false, false, $channel);
  44929. if (PEAR::isError($version)) {
  44930. break; // skipit
  44931. }
  44932. $url = sprintf('%s'.'r/%s/%s.xml',
  44933. $base,
  44934. strtolower($packageitem['_content']),
  44935. $version);
  44936. $info = $this->_rest->retrieveData($url, false, false, $channel);
  44937. if (PEAR::isError($info)) {
  44938. break; // skipit
  44939. }
  44940. $packagelist[$i]['info'] = $info;
  44941. }
  44942. PEAR::popErrorHandling();
  44943. }
  44944. return $packagelist;
  44945. }
  44946. function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false)
  44947. {
  44948. $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
  44949. if (PEAR::isError($packagelist)) {
  44950. return $packagelist;
  44951. }
  44952. if ($this->_rest->config->get('verbose') > 0) {
  44953. $ui = &PEAR_Frontend::singleton();
  44954. $ui->log('Retrieving data...0%', true);
  44955. }
  44956. $ret = array();
  44957. if (!is_array($packagelist) || !isset($packagelist['p'])) {
  44958. return $ret;
  44959. }
  44960. if (!is_array($packagelist['p'])) {
  44961. $packagelist['p'] = array($packagelist['p']);
  44962. }
  44963. // only search-packagename = quicksearch !
  44964. if ($searchpackage && (!$searchsummary || empty($searchpackage))) {
  44965. $newpackagelist = array();
  44966. foreach ($packagelist['p'] as $package) {
  44967. if (!empty($searchpackage) && stristr($package, $searchpackage) !== false) {
  44968. $newpackagelist[] = $package;
  44969. }
  44970. }
  44971. $packagelist['p'] = $newpackagelist;
  44972. }
  44973. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  44974. $next = .1;
  44975. foreach ($packagelist['p'] as $progress => $package) {
  44976. if ($this->_rest->config->get('verbose') > 0) {
  44977. if ($progress / count($packagelist['p']) >= $next) {
  44978. if ($next == .5) {
  44979. $ui->log('50%', false);
  44980. } else {
  44981. $ui->log('.', false);
  44982. }
  44983. $next += .1;
  44984. }
  44985. }
  44986. if ($basic) { // remote-list command
  44987. if ($dostable) {
  44988. $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
  44989. '/stable.txt', false, false, $channel);
  44990. } else {
  44991. $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
  44992. '/latest.txt', false, false, $channel);
  44993. }
  44994. if (PEAR::isError($latest)) {
  44995. $latest = false;
  44996. }
  44997. $info = array('stable' => $latest);
  44998. } else { // list-all command
  44999. $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
  45000. if (PEAR::isError($inf)) {
  45001. PEAR::popErrorHandling();
  45002. return $inf;
  45003. }
  45004. if ($searchpackage) {
  45005. $found = (!empty($searchpackage) && stristr($package, $searchpackage) !== false);
  45006. if (!$found && !(isset($searchsummary) && !empty($searchsummary)
  45007. && (stristr($inf['s'], $searchsummary) !== false
  45008. || stristr($inf['d'], $searchsummary) !== false)))
  45009. {
  45010. continue;
  45011. };
  45012. }
  45013. $releases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
  45014. '/allreleases.xml', false, false, $channel);
  45015. if (PEAR::isError($releases)) {
  45016. continue;
  45017. }
  45018. if (!isset($releases['r'][0])) {
  45019. $releases['r'] = array($releases['r']);
  45020. }
  45021. unset($latest);
  45022. unset($unstable);
  45023. unset($stable);
  45024. unset($state);
  45025. foreach ($releases['r'] as $release) {
  45026. if (!isset($latest)) {
  45027. if ($dostable && $release['s'] == 'stable') {
  45028. $latest = $release['v'];
  45029. $state = 'stable';
  45030. }
  45031. if (!$dostable) {
  45032. $latest = $release['v'];
  45033. $state = $release['s'];
  45034. }
  45035. }
  45036. if (!isset($stable) && $release['s'] == 'stable') {
  45037. $stable = $release['v'];
  45038. if (!isset($unstable)) {
  45039. $unstable = $stable;
  45040. }
  45041. }
  45042. if (!isset($unstable) && $release['s'] != 'stable') {
  45043. $latest = $unstable = $release['v'];
  45044. $state = $release['s'];
  45045. }
  45046. if (isset($latest) && !isset($state)) {
  45047. $state = $release['s'];
  45048. }
  45049. if (isset($latest) && isset($stable) && isset($unstable)) {
  45050. break;
  45051. }
  45052. }
  45053. $deps = array();
  45054. if (!isset($unstable)) {
  45055. $unstable = false;
  45056. $state = 'stable';
  45057. if (isset($stable)) {
  45058. $latest = $unstable = $stable;
  45059. }
  45060. } else {
  45061. $latest = $unstable;
  45062. }
  45063. if (!isset($latest)) {
  45064. $latest = false;
  45065. }
  45066. if ($latest) {
  45067. $d = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' .
  45068. $latest . '.txt', false, false, $channel);
  45069. if (!PEAR::isError($d)) {
  45070. $d = unserialize($d);
  45071. if ($d) {
  45072. if (isset($d['required'])) {
  45073. if (!class_exists('PEAR_PackageFile_v2')) {
  45074. require_once 'PEAR/PackageFile/v2.php';
  45075. }
  45076. if (!isset($pf)) {
  45077. $pf = new PEAR_PackageFile_v2;
  45078. }
  45079. $pf->setDeps($d);
  45080. $tdeps = $pf->getDeps();
  45081. } else {
  45082. $tdeps = $d;
  45083. }
  45084. foreach ($tdeps as $dep) {
  45085. if ($dep['type'] !== 'pkg') {
  45086. continue;
  45087. }
  45088. $deps[] = $dep;
  45089. }
  45090. }
  45091. }
  45092. }
  45093. if (!isset($stable)) {
  45094. $stable = '-n/a-';
  45095. }
  45096. if (!$searchpackage) {
  45097. $info = array('stable' => $latest, 'summary' => $inf['s'], 'description' =>
  45098. $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'],
  45099. 'unstable' => $unstable, 'state' => $state);
  45100. } else {
  45101. $info = array('stable' => $stable, 'summary' => $inf['s'], 'description' =>
  45102. $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'],
  45103. 'unstable' => $unstable, 'state' => $state);
  45104. }
  45105. }
  45106. $ret[$package] = $info;
  45107. }
  45108. PEAR::popErrorHandling();
  45109. return $ret;
  45110. }
  45111. function listLatestUpgrades($base, $pref_state, $installed, $channel, &$reg)
  45112. {
  45113. $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
  45114. if (PEAR::isError($packagelist)) {
  45115. return $packagelist;
  45116. }
  45117. $ret = array();
  45118. if (!is_array($packagelist) || !isset($packagelist['p'])) {
  45119. return $ret;
  45120. }
  45121. if (!is_array($packagelist['p'])) {
  45122. $packagelist['p'] = array($packagelist['p']);
  45123. }
  45124. foreach ($packagelist['p'] as $package) {
  45125. if (!isset($installed[strtolower($package)])) {
  45126. continue;
  45127. }
  45128. $inst_version = $reg->packageInfo($package, 'version', $channel);
  45129. $inst_state = $reg->packageInfo($package, 'release_state', $channel);
  45130. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  45131. $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
  45132. '/allreleases.xml', false, false, $channel);
  45133. PEAR::popErrorHandling();
  45134. if (PEAR::isError($info)) {
  45135. continue; // no remote releases
  45136. }
  45137. if (!isset($info['r'])) {
  45138. continue;
  45139. }
  45140. $release = $found = false;
  45141. if (!is_array($info['r']) || !isset($info['r'][0])) {
  45142. $info['r'] = array($info['r']);
  45143. }
  45144. // $info['r'] is sorted by version number
  45145. usort($info['r'], array($this, '_sortReleasesByVersionNumber'));
  45146. foreach ($info['r'] as $release) {
  45147. if ($inst_version && version_compare($release['v'], $inst_version, '<=')) {
  45148. // not newer than the one installed
  45149. break;
  45150. }
  45151. // new version > installed version
  45152. if (!$pref_state) {
  45153. // every state is a good state
  45154. $found = true;
  45155. break;
  45156. } else {
  45157. $new_state = $release['s'];
  45158. // if new state >= installed state: go
  45159. if (in_array($new_state, $this->betterStates($inst_state, true))) {
  45160. $found = true;
  45161. break;
  45162. } else {
  45163. // only allow to lower the state of package,
  45164. // if new state >= preferred state: go
  45165. if (in_array($new_state, $this->betterStates($pref_state, true))) {
  45166. $found = true;
  45167. break;
  45168. }
  45169. }
  45170. }
  45171. }
  45172. if (!$found) {
  45173. continue;
  45174. }
  45175. $relinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' .
  45176. $release['v'] . '.xml', false, false, $channel);
  45177. if (PEAR::isError($relinfo)) {
  45178. return $relinfo;
  45179. }
  45180. $ret[$package] = array(
  45181. 'version' => $release['v'],
  45182. 'state' => $release['s'],
  45183. 'filesize' => $relinfo['f'],
  45184. );
  45185. }
  45186. return $ret;
  45187. }
  45188. function packageInfo($base, $package, $channel = false)
  45189. {
  45190. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  45191. $pinfo = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
  45192. if (PEAR::isError($pinfo)) {
  45193. PEAR::popErrorHandling();
  45194. return PEAR::raiseError('Unknown package: "' . $package . '" in channel "' . $channel . '"' . "\n". 'Debug: ' .
  45195. $pinfo->getMessage());
  45196. }
  45197. $releases = array();
  45198. $allreleases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
  45199. '/allreleases.xml', false, false, $channel);
  45200. if (!PEAR::isError($allreleases)) {
  45201. if (!class_exists('PEAR_PackageFile_v2')) {
  45202. require_once 'PEAR/PackageFile/v2.php';
  45203. }
  45204. if (!is_array($allreleases['r']) || !isset($allreleases['r'][0])) {
  45205. $allreleases['r'] = array($allreleases['r']);
  45206. }
  45207. $pf = new PEAR_PackageFile_v2;
  45208. foreach ($allreleases['r'] as $release) {
  45209. $ds = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' .
  45210. $release['v'] . '.txt', false, false, $channel);
  45211. if (PEAR::isError($ds)) {
  45212. continue;
  45213. }
  45214. if (!isset($latest)) {
  45215. $latest = $release['v'];
  45216. }
  45217. $pf->setDeps(unserialize($ds));
  45218. $ds = $pf->getDeps();
  45219. $info = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package)
  45220. . '/' . $release['v'] . '.xml', false, false, $channel);
  45221. if (PEAR::isError($info)) {
  45222. continue;
  45223. }
  45224. $releases[$release['v']] = array(
  45225. 'doneby' => $info['m'],
  45226. 'license' => $info['l'],
  45227. 'summary' => $info['s'],
  45228. 'description' => $info['d'],
  45229. 'releasedate' => $info['da'],
  45230. 'releasenotes' => $info['n'],
  45231. 'state' => $release['s'],
  45232. 'deps' => $ds ? $ds : array(),
  45233. );
  45234. }
  45235. } else {
  45236. $latest = '';
  45237. }
  45238. PEAR::popErrorHandling();
  45239. if (isset($pinfo['dc']) && isset($pinfo['dp'])) {
  45240. if (is_array($pinfo['dp'])) {
  45241. $deprecated = array('channel' => (string) $pinfo['dc'],
  45242. 'package' => trim($pinfo['dp']['_content']));
  45243. } else {
  45244. $deprecated = array('channel' => (string) $pinfo['dc'],
  45245. 'package' => trim($pinfo['dp']));
  45246. }
  45247. } else {
  45248. $deprecated = false;
  45249. }
  45250. if (!isset($latest)) {
  45251. $latest = '';
  45252. }
  45253. return array(
  45254. 'name' => $pinfo['n'],
  45255. 'channel' => $pinfo['c'],
  45256. 'category' => $pinfo['ca']['_content'],
  45257. 'stable' => $latest,
  45258. 'license' => $pinfo['l'],
  45259. 'summary' => $pinfo['s'],
  45260. 'description' => $pinfo['d'],
  45261. 'releases' => $releases,
  45262. 'deprecated' => $deprecated,
  45263. );
  45264. }
  45265. /**
  45266. * Return an array containing all of the states that are more stable than
  45267. * or equal to the passed in state
  45268. *
  45269. * @param string Release state
  45270. * @param boolean Determines whether to include $state in the list
  45271. * @return false|array False if $state is not a valid release state
  45272. */
  45273. function betterStates($state, $include = false)
  45274. {
  45275. static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
  45276. $i = array_search($state, $states);
  45277. if ($i === false) {
  45278. return false;
  45279. }
  45280. if ($include) {
  45281. $i--;
  45282. }
  45283. return array_slice($states, $i + 1);
  45284. }
  45285. /**
  45286. * Sort releases by version number
  45287. *
  45288. * @access private
  45289. */
  45290. function _sortReleasesByVersionNumber($a, $b)
  45291. {
  45292. if (version_compare($a['v'], $b['v'], '=')) {
  45293. return 0;
  45294. }
  45295. if (version_compare($a['v'], $b['v'], '>')) {
  45296. return -1;
  45297. }
  45298. if (version_compare($a['v'], $b['v'], '<')) {
  45299. return 1;
  45300. }
  45301. }
  45302. }
  45303. ���������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/REST/11.php�����������������������������������������������������������������������0000644�0001750�0001750�00000025751�14720722517�014536� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  45304. /**
  45305. * PEAR_REST_11 - implement faster list-all/remote-list command
  45306. *
  45307. * PHP versions 4 and 5
  45308. *
  45309. * @category pear
  45310. * @package PEAR
  45311. * @author Greg Beaver <cellog@php.net>
  45312. * @copyright 1997-2009 The Authors
  45313. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  45314. * @link http://pear.php.net/package/PEAR
  45315. * @since File available since Release 1.4.3
  45316. */
  45317. /**
  45318. * For downloading REST xml/txt files
  45319. */
  45320. require_once 'PEAR/REST.php';
  45321. /**
  45322. * Implement REST 1.1
  45323. *
  45324. * @category pear
  45325. * @package PEAR
  45326. * @author Greg Beaver <cellog@php.net>
  45327. * @copyright 1997-2009 The Authors
  45328. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  45329. * @version Release: 1.10.16
  45330. * @link http://pear.php.net/package/PEAR
  45331. * @since Class available since Release 1.4.3
  45332. */
  45333. class PEAR_REST_11
  45334. {
  45335. /**
  45336. * @var PEAR_REST
  45337. */
  45338. var $_rest;
  45339. function __construct($config, $options = array())
  45340. {
  45341. $this->_rest = new PEAR_REST($config, $options);
  45342. }
  45343. function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false)
  45344. {
  45345. $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml', false, false, $channel);
  45346. if (PEAR::isError($categorylist)) {
  45347. return $categorylist;
  45348. }
  45349. $ret = array();
  45350. if (!is_array($categorylist['c']) || !isset($categorylist['c'][0])) {
  45351. $categorylist['c'] = array($categorylist['c']);
  45352. }
  45353. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  45354. foreach ($categorylist['c'] as $progress => $category) {
  45355. $category = $category['_content'];
  45356. $packagesinfo = $this->_rest->retrieveData($base .
  45357. 'c/' . urlencode($category) . '/packagesinfo.xml', false, false, $channel);
  45358. if (PEAR::isError($packagesinfo)) {
  45359. continue;
  45360. }
  45361. if (!is_array($packagesinfo) || !isset($packagesinfo['pi'])) {
  45362. continue;
  45363. }
  45364. if (!is_array($packagesinfo['pi']) || !isset($packagesinfo['pi'][0])) {
  45365. $packagesinfo['pi'] = array($packagesinfo['pi']);
  45366. }
  45367. foreach ($packagesinfo['pi'] as $packageinfo) {
  45368. if (empty($packageinfo)) {
  45369. continue;
  45370. }
  45371. $info = $packageinfo['p'];
  45372. $package = $info['n'];
  45373. $releases = isset($packageinfo['a']) ? $packageinfo['a'] : false;
  45374. unset($latest);
  45375. unset($unstable);
  45376. unset($stable);
  45377. unset($state);
  45378. if ($releases) {
  45379. if (!isset($releases['r'][0])) {
  45380. $releases['r'] = array($releases['r']);
  45381. }
  45382. foreach ($releases['r'] as $release) {
  45383. if (!isset($latest)) {
  45384. if ($dostable && $release['s'] == 'stable') {
  45385. $latest = $release['v'];
  45386. $state = 'stable';
  45387. }
  45388. if (!$dostable) {
  45389. $latest = $release['v'];
  45390. $state = $release['s'];
  45391. }
  45392. }
  45393. if (!isset($stable) && $release['s'] == 'stable') {
  45394. $stable = $release['v'];
  45395. if (!isset($unstable)) {
  45396. $unstable = $stable;
  45397. }
  45398. }
  45399. if (!isset($unstable) && $release['s'] != 'stable') {
  45400. $unstable = $release['v'];
  45401. $state = $release['s'];
  45402. }
  45403. if (isset($latest) && !isset($state)) {
  45404. $state = $release['s'];
  45405. }
  45406. if (isset($latest) && isset($stable) && isset($unstable)) {
  45407. break;
  45408. }
  45409. }
  45410. }
  45411. if ($basic) { // remote-list command
  45412. if (!isset($latest)) {
  45413. $latest = false;
  45414. }
  45415. if ($dostable) {
  45416. // $state is not set if there are no releases
  45417. if (isset($state) && $state == 'stable') {
  45418. $ret[$package] = array('stable' => $latest);
  45419. } else {
  45420. $ret[$package] = array('stable' => '-n/a-');
  45421. }
  45422. } else {
  45423. $ret[$package] = array('stable' => $latest);
  45424. }
  45425. continue;
  45426. }
  45427. // list-all command
  45428. if (!isset($unstable)) {
  45429. $unstable = false;
  45430. $state = 'stable';
  45431. if (isset($stable)) {
  45432. $latest = $unstable = $stable;
  45433. }
  45434. } else {
  45435. $latest = $unstable;
  45436. }
  45437. if (!isset($latest)) {
  45438. $latest = false;
  45439. }
  45440. $deps = array();
  45441. if ($latest && isset($packageinfo['deps'])) {
  45442. if (!is_array($packageinfo['deps']) ||
  45443. !isset($packageinfo['deps'][0])
  45444. ) {
  45445. $packageinfo['deps'] = array($packageinfo['deps']);
  45446. }
  45447. $d = false;
  45448. foreach ($packageinfo['deps'] as $dep) {
  45449. if ($dep['v'] == $latest) {
  45450. $d = unserialize($dep['d']);
  45451. }
  45452. }
  45453. if ($d) {
  45454. if (isset($d['required'])) {
  45455. if (!class_exists('PEAR_PackageFile_v2')) {
  45456. require_once 'PEAR/PackageFile/v2.php';
  45457. }
  45458. if (!isset($pf)) {
  45459. $pf = new PEAR_PackageFile_v2;
  45460. }
  45461. $pf->setDeps($d);
  45462. $tdeps = $pf->getDeps();
  45463. } else {
  45464. $tdeps = $d;
  45465. }
  45466. foreach ($tdeps as $dep) {
  45467. if ($dep['type'] !== 'pkg') {
  45468. continue;
  45469. }
  45470. $deps[] = $dep;
  45471. }
  45472. }
  45473. }
  45474. $info = array(
  45475. 'stable' => $latest,
  45476. 'summary' => $info['s'],
  45477. 'description' => $info['d'],
  45478. 'deps' => $deps,
  45479. 'category' => $info['ca']['_content'],
  45480. 'unstable' => $unstable,
  45481. 'state' => $state
  45482. );
  45483. $ret[$package] = $info;
  45484. }
  45485. }
  45486. PEAR::popErrorHandling();
  45487. return $ret;
  45488. }
  45489. /**
  45490. * List all categories of a REST server
  45491. *
  45492. * @param string $base base URL of the server
  45493. * @return array of categorynames
  45494. */
  45495. function listCategories($base, $channel = false)
  45496. {
  45497. $categorylist = $this->_rest->retrieveData($base . 'c/categories.xml', false, false, $channel);
  45498. if (PEAR::isError($categorylist)) {
  45499. return $categorylist;
  45500. }
  45501. if (!is_array($categorylist) || !isset($categorylist['c'])) {
  45502. return array();
  45503. }
  45504. if (isset($categorylist['c']['_content'])) {
  45505. // only 1 category
  45506. $categorylist['c'] = array($categorylist['c']);
  45507. }
  45508. return $categorylist['c'];
  45509. }
  45510. /**
  45511. * List packages in a category of a REST server
  45512. *
  45513. * @param string $base base URL of the server
  45514. * @param string $category name of the category
  45515. * @param boolean $info also download full package info
  45516. * @return array of packagenames
  45517. */
  45518. function listCategory($base, $category, $info = false, $channel = false)
  45519. {
  45520. if ($info == false) {
  45521. $url = '%s'.'c/%s/packages.xml';
  45522. } else {
  45523. $url = '%s'.'c/%s/packagesinfo.xml';
  45524. }
  45525. $url = sprintf($url,
  45526. $base,
  45527. urlencode($category));
  45528. // gives '404 Not Found' error when category doesn't exist
  45529. $packagelist = $this->_rest->retrieveData($url, false, false, $channel);
  45530. if (PEAR::isError($packagelist)) {
  45531. return $packagelist;
  45532. }
  45533. if (!is_array($packagelist)) {
  45534. return array();
  45535. }
  45536. if ($info == false) {
  45537. if (!isset($packagelist['p'])) {
  45538. return array();
  45539. }
  45540. if (!is_array($packagelist['p']) ||
  45541. !isset($packagelist['p'][0])) { // only 1 pkg
  45542. $packagelist = array($packagelist['p']);
  45543. } else {
  45544. $packagelist = $packagelist['p'];
  45545. }
  45546. return $packagelist;
  45547. }
  45548. // info == true
  45549. if (!isset($packagelist['pi'])) {
  45550. return array();
  45551. }
  45552. if (!is_array($packagelist['pi']) ||
  45553. !isset($packagelist['pi'][0])) { // only 1 pkg
  45554. $packagelist_pre = array($packagelist['pi']);
  45555. } else {
  45556. $packagelist_pre = $packagelist['pi'];
  45557. }
  45558. $packagelist = array();
  45559. foreach ($packagelist_pre as $i => $item) {
  45560. // compatibility with r/<latest.txt>.xml
  45561. if (isset($item['a']['r'][0])) {
  45562. // multiple releases
  45563. $item['p']['v'] = $item['a']['r'][0]['v'];
  45564. $item['p']['st'] = $item['a']['r'][0]['s'];
  45565. } elseif (isset($item['a'])) {
  45566. // first and only release
  45567. $item['p']['v'] = $item['a']['r']['v'];
  45568. $item['p']['st'] = $item['a']['r']['s'];
  45569. }
  45570. $packagelist[$i] = array('attribs' => $item['p']['r'],
  45571. '_content' => $item['p']['n'],
  45572. 'info' => $item['p']);
  45573. }
  45574. return $packagelist;
  45575. }
  45576. /**
  45577. * Return an array containing all of the states that are more stable than
  45578. * or equal to the passed in state
  45579. *
  45580. * @param string Release state
  45581. * @param boolean Determines whether to include $state in the list
  45582. * @return false|array False if $state is not a valid release state
  45583. */
  45584. function betterStates($state, $include = false)
  45585. {
  45586. static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
  45587. $i = array_search($state, $states);
  45588. if ($i === false) {
  45589. return false;
  45590. }
  45591. if ($include) {
  45592. $i--;
  45593. }
  45594. return array_slice($states, $i + 1);
  45595. }
  45596. }
  45597. ?>
  45598. �����������������������PEAR-1.10.16/PEAR/REST/13.php�����������������������������������������������������������������������0000644�0001750�0001750�00000035427�14720722517�014541� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  45599. /**
  45600. * PEAR_REST_13
  45601. *
  45602. * PHP versions 4 and 5
  45603. *
  45604. * @category pear
  45605. * @package PEAR
  45606. * @author Greg Beaver <cellog@php.net>
  45607. * @copyright 1997-2009 The Authors
  45608. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  45609. * @link http://pear.php.net/package/PEAR
  45610. * @since File available since Release 1.4.0a12
  45611. */
  45612. /**
  45613. * For downloading REST xml/txt files
  45614. */
  45615. require_once 'PEAR/REST.php';
  45616. require_once 'PEAR/REST/10.php';
  45617. /**
  45618. * Implement REST 1.3
  45619. *
  45620. * @category pear
  45621. * @package PEAR
  45622. * @author Greg Beaver <cellog@php.net>
  45623. * @copyright 1997-2009 The Authors
  45624. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  45625. * @version Release: 1.10.16
  45626. * @link http://pear.php.net/package/PEAR
  45627. * @since Class available since Release 1.4.0a12
  45628. */
  45629. class PEAR_REST_13 extends PEAR_REST_10
  45630. {
  45631. /**
  45632. * Retrieve information about a remote package to be downloaded from a REST server
  45633. *
  45634. * This is smart enough to resolve the minimum PHP version dependency prior to download
  45635. * @param string $base The uri to prepend to all REST calls
  45636. * @param array $packageinfo an array of format:
  45637. * <pre>
  45638. * array(
  45639. * 'package' => 'packagename',
  45640. * 'channel' => 'channelname',
  45641. * ['state' => 'alpha' (or valid state),]
  45642. * -or-
  45643. * ['version' => '1.whatever']
  45644. * </pre>
  45645. * @param string $prefstate Current preferred_state config variable value
  45646. * @param bool $installed the installed version of this package to compare against
  45647. * @return array|false|PEAR_Error see {@link _returnDownloadURL()}
  45648. */
  45649. function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false)
  45650. {
  45651. $states = $this->betterStates($prefstate, true);
  45652. if (!$states) {
  45653. return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
  45654. }
  45655. $channel = $packageinfo['channel'];
  45656. $package = $packageinfo['package'];
  45657. $state = isset($packageinfo['state']) ? $packageinfo['state'] : null;
  45658. $version = isset($packageinfo['version']) ? $packageinfo['version'] : null;
  45659. $restFile = $base . 'r/' . strtolower($package) . '/allreleases2.xml';
  45660. $info = $this->_rest->retrieveData($restFile, false, false, $channel);
  45661. if (PEAR::isError($info)) {
  45662. return PEAR::raiseError('No releases available for package "' .
  45663. $channel . '/' . $package . '"');
  45664. }
  45665. if (!isset($info['r'])) {
  45666. return false;
  45667. }
  45668. $release = $found = false;
  45669. if (!is_array($info['r']) || !isset($info['r'][0])) {
  45670. $info['r'] = array($info['r']);
  45671. }
  45672. $skippedphp = false;
  45673. foreach ($info['r'] as $release) {
  45674. if (!isset($this->_rest->_options['force']) && ($installed &&
  45675. version_compare($release['v'], $installed, '<'))) {
  45676. continue;
  45677. }
  45678. if (isset($state)) {
  45679. // try our preferred state first
  45680. if ($release['s'] == $state) {
  45681. if (!isset($version) && version_compare($release['m'], phpversion(), '>')) {
  45682. // skip releases that require a PHP version newer than our PHP version
  45683. $skippedphp = $release;
  45684. continue;
  45685. }
  45686. $found = true;
  45687. break;
  45688. }
  45689. // see if there is something newer and more stable
  45690. // bug #7221
  45691. if (in_array($release['s'], $this->betterStates($state), true)) {
  45692. if (!isset($version) && version_compare($release['m'], phpversion(), '>')) {
  45693. // skip releases that require a PHP version newer than our PHP version
  45694. $skippedphp = $release;
  45695. continue;
  45696. }
  45697. $found = true;
  45698. break;
  45699. }
  45700. } elseif (isset($version)) {
  45701. if ($release['v'] == $version) {
  45702. if (!isset($this->_rest->_options['force']) &&
  45703. !isset($version) &&
  45704. version_compare($release['m'], phpversion(), '>')) {
  45705. // skip releases that require a PHP version newer than our PHP version
  45706. $skippedphp = $release;
  45707. continue;
  45708. }
  45709. $found = true;
  45710. break;
  45711. }
  45712. } else {
  45713. if (in_array($release['s'], $states)) {
  45714. if (version_compare($release['m'], phpversion(), '>')) {
  45715. // skip releases that require a PHP version newer than our PHP version
  45716. $skippedphp = $release;
  45717. continue;
  45718. }
  45719. $found = true;
  45720. break;
  45721. }
  45722. }
  45723. }
  45724. if (!$found && $skippedphp) {
  45725. $found = null;
  45726. }
  45727. return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp, $channel);
  45728. }
  45729. function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage,
  45730. $prefstate = 'stable', $installed = false, $channel = false)
  45731. {
  45732. $states = $this->betterStates($prefstate, true);
  45733. if (!$states) {
  45734. return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
  45735. }
  45736. $channel = $dependency['channel'];
  45737. $package = $dependency['name'];
  45738. $state = isset($dependency['state']) ? $dependency['state'] : null;
  45739. $version = isset($dependency['version']) ? $dependency['version'] : null;
  45740. $restFile = $base . 'r/' . strtolower($package) .'/allreleases2.xml';
  45741. $info = $this->_rest->retrieveData($restFile, false, false, $channel);
  45742. if (PEAR::isError($info)) {
  45743. return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package']
  45744. . '" dependency "' . $channel . '/' . $package . '" has no releases');
  45745. }
  45746. if (!is_array($info) || !isset($info['r'])) {
  45747. return false;
  45748. }
  45749. $exclude = array();
  45750. $min = $max = $recommended = false;
  45751. if ($xsdversion == '1.0') {
  45752. $pinfo['package'] = $dependency['name'];
  45753. $pinfo['channel'] = 'pear.php.net'; // this is always true - don't change this
  45754. switch ($dependency['rel']) {
  45755. case 'ge' :
  45756. $min = $dependency['version'];
  45757. break;
  45758. case 'gt' :
  45759. $min = $dependency['version'];
  45760. $exclude = array($dependency['version']);
  45761. break;
  45762. case 'eq' :
  45763. $recommended = $dependency['version'];
  45764. break;
  45765. case 'lt' :
  45766. $max = $dependency['version'];
  45767. $exclude = array($dependency['version']);
  45768. break;
  45769. case 'le' :
  45770. $max = $dependency['version'];
  45771. break;
  45772. case 'ne' :
  45773. $exclude = array($dependency['version']);
  45774. break;
  45775. }
  45776. } else {
  45777. $pinfo['package'] = $dependency['name'];
  45778. $min = isset($dependency['min']) ? $dependency['min'] : false;
  45779. $max = isset($dependency['max']) ? $dependency['max'] : false;
  45780. $recommended = isset($dependency['recommended']) ?
  45781. $dependency['recommended'] : false;
  45782. if (isset($dependency['exclude'])) {
  45783. if (!isset($dependency['exclude'][0])) {
  45784. $exclude = array($dependency['exclude']);
  45785. }
  45786. }
  45787. }
  45788. $skippedphp = $found = $release = false;
  45789. if (!is_array($info['r']) || !isset($info['r'][0])) {
  45790. $info['r'] = array($info['r']);
  45791. }
  45792. foreach ($info['r'] as $release) {
  45793. if (!isset($this->_rest->_options['force']) && ($installed &&
  45794. version_compare($release['v'], $installed, '<'))) {
  45795. continue;
  45796. }
  45797. if (in_array($release['v'], $exclude)) { // skip excluded versions
  45798. continue;
  45799. }
  45800. // allow newer releases to say "I'm OK with the dependent package"
  45801. if ($xsdversion == '2.0' && isset($release['co'])) {
  45802. if (!is_array($release['co']) || !isset($release['co'][0])) {
  45803. $release['co'] = array($release['co']);
  45804. }
  45805. foreach ($release['co'] as $entry) {
  45806. if (isset($entry['x']) && !is_array($entry['x'])) {
  45807. $entry['x'] = array($entry['x']);
  45808. } elseif (!isset($entry['x'])) {
  45809. $entry['x'] = array();
  45810. }
  45811. if ($entry['c'] == $deppackage['channel'] &&
  45812. strtolower($entry['p']) == strtolower($deppackage['package']) &&
  45813. version_compare($deppackage['version'], $entry['min'], '>=') &&
  45814. version_compare($deppackage['version'], $entry['max'], '<=') &&
  45815. !in_array($release['v'], $entry['x'])) {
  45816. if (version_compare($release['m'], phpversion(), '>')) {
  45817. // skip dependency releases that require a PHP version
  45818. // newer than our PHP version
  45819. $skippedphp = $release;
  45820. continue;
  45821. }
  45822. $recommended = $release['v'];
  45823. break;
  45824. }
  45825. }
  45826. }
  45827. if ($recommended) {
  45828. if ($release['v'] != $recommended) { // if we want a specific
  45829. // version, then skip all others
  45830. continue;
  45831. }
  45832. if (!in_array($release['s'], $states)) {
  45833. // the stability is too low, but we must return the
  45834. // recommended version if possible
  45835. return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel);
  45836. }
  45837. }
  45838. if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions
  45839. continue;
  45840. }
  45841. if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions
  45842. continue;
  45843. }
  45844. if ($installed && version_compare($release['v'], $installed, '<')) {
  45845. continue;
  45846. }
  45847. if (in_array($release['s'], $states)) { // if in the preferred state...
  45848. if (version_compare($release['m'], phpversion(), '>')) {
  45849. // skip dependency releases that require a PHP version
  45850. // newer than our PHP version
  45851. $skippedphp = $release;
  45852. continue;
  45853. }
  45854. $found = true; // ... then use it
  45855. break;
  45856. }
  45857. }
  45858. if (!$found && $skippedphp) {
  45859. $found = null;
  45860. }
  45861. return $this->_returnDownloadURL($base, $package, $release, $info, $found, $skippedphp, $channel);
  45862. }
  45863. /**
  45864. * List package upgrades but take the PHP version into account.
  45865. */
  45866. function listLatestUpgrades($base, $pref_state, $installed, $channel, &$reg)
  45867. {
  45868. $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
  45869. if (PEAR::isError($packagelist)) {
  45870. return $packagelist;
  45871. }
  45872. $ret = array();
  45873. if (!is_array($packagelist) || !isset($packagelist['p'])) {
  45874. return $ret;
  45875. }
  45876. if (!is_array($packagelist['p'])) {
  45877. $packagelist['p'] = array($packagelist['p']);
  45878. }
  45879. foreach ($packagelist['p'] as $package) {
  45880. if (!isset($installed[strtolower($package)])) {
  45881. continue;
  45882. }
  45883. $inst_version = $reg->packageInfo($package, 'version', $channel);
  45884. $inst_state = $reg->packageInfo($package, 'release_state', $channel);
  45885. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  45886. $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
  45887. '/allreleases2.xml', false, false, $channel);
  45888. PEAR::popErrorHandling();
  45889. if (PEAR::isError($info)) {
  45890. continue; // no remote releases
  45891. }
  45892. if (!isset($info['r'])) {
  45893. continue;
  45894. }
  45895. $release = $found = false;
  45896. if (!is_array($info['r']) || !isset($info['r'][0])) {
  45897. $info['r'] = array($info['r']);
  45898. }
  45899. // $info['r'] is sorted by version number
  45900. usort($info['r'], array($this, '_sortReleasesByVersionNumber'));
  45901. foreach ($info['r'] as $release) {
  45902. if ($inst_version && version_compare($release['v'], $inst_version, '<=')) {
  45903. // not newer than the one installed
  45904. break;
  45905. }
  45906. if (version_compare($release['m'], phpversion(), '>')) {
  45907. // skip dependency releases that require a PHP version
  45908. // newer than our PHP version
  45909. continue;
  45910. }
  45911. // new version > installed version
  45912. if (!$pref_state) {
  45913. // every state is a good state
  45914. $found = true;
  45915. break;
  45916. } else {
  45917. $new_state = $release['s'];
  45918. // if new state >= installed state: go
  45919. if (in_array($new_state, $this->betterStates($inst_state, true))) {
  45920. $found = true;
  45921. break;
  45922. } else {
  45923. // only allow to lower the state of package,
  45924. // if new state >= preferred state: go
  45925. if (in_array($new_state, $this->betterStates($pref_state, true))) {
  45926. $found = true;
  45927. break;
  45928. }
  45929. }
  45930. }
  45931. }
  45932. if (!$found) {
  45933. continue;
  45934. }
  45935. $relinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' .
  45936. $release['v'] . '.xml', false, false, $channel);
  45937. if (PEAR::isError($relinfo)) {
  45938. return $relinfo;
  45939. }
  45940. $ret[$package] = array(
  45941. 'version' => $release['v'],
  45942. 'state' => $release['s'],
  45943. 'filesize' => $relinfo['f'],
  45944. );
  45945. }
  45946. return $ret;
  45947. }
  45948. }�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Task/Postinstallscript/rw.php�����������������������������������������������������0000644�0001750�0001750�00000014034�14720722517�020623� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  45949. /**
  45950. * <tasks:postinstallscript> - read/write version
  45951. *
  45952. * PHP versions 4 and 5
  45953. *
  45954. * @category pear
  45955. * @package PEAR
  45956. * @author Greg Beaver <cellog@php.net>
  45957. * @copyright 1997-2009 The Authors
  45958. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  45959. * @link http://pear.php.net/package/PEAR
  45960. * @since File available since Release 1.4.0a10
  45961. */
  45962. /**
  45963. * Base class
  45964. */
  45965. require_once 'PEAR/Task/Postinstallscript.php';
  45966. /**
  45967. * Abstracts the postinstallscript file task xml.
  45968. * @category pear
  45969. * @package PEAR
  45970. * @author Greg Beaver <cellog@php.net>
  45971. * @copyright 1997-2009 The Authors
  45972. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  45973. * @version Release: 1.10.16
  45974. * @link http://pear.php.net/package/PEAR
  45975. * @since Class available since Release 1.4.0a10
  45976. */
  45977. class PEAR_Task_Postinstallscript_rw extends PEAR_Task_Postinstallscript
  45978. {
  45979. /**
  45980. * parent package file object
  45981. *
  45982. * @var PEAR_PackageFile_v2_rw
  45983. */
  45984. public $_pkg;
  45985. /**
  45986. * Enter description here...
  45987. *
  45988. * @param PEAR_PackageFile_v2_rw $pkg Package
  45989. * @param PEAR_Config $config Config
  45990. * @param PEAR_Frontend $logger Logger
  45991. * @param array $fileXml XML
  45992. *
  45993. * @return PEAR_Task_Postinstallscript_rw
  45994. */
  45995. function __construct(&$pkg, &$config, &$logger, $fileXml)
  45996. {
  45997. parent::__construct($config, $logger, PEAR_TASK_PACKAGE);
  45998. $this->_contents = $fileXml;
  45999. $this->_pkg = &$pkg;
  46000. $this->_params = array();
  46001. }
  46002. public function validate()
  46003. {
  46004. return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents);
  46005. }
  46006. public function getName()
  46007. {
  46008. return 'postinstallscript';
  46009. }
  46010. /**
  46011. * add a simple <paramgroup> to the post-install script
  46012. *
  46013. * Order is significant, so call this method in the same
  46014. * sequence the users should see the paramgroups. The $params
  46015. * parameter should either be the result of a call to {@link getParam()}
  46016. * or an array of calls to getParam().
  46017. *
  46018. * Use {@link addConditionTypeGroup()} to add a <paramgroup> containing
  46019. * a <conditiontype> tag
  46020. *
  46021. * @param string $id <paramgroup> id as seen by the script
  46022. * @param array|false $params array of getParam() calls, or false for no params
  46023. * @param string|false $instructions
  46024. */
  46025. public function addParamGroup($id, $params = false, $instructions = false)
  46026. {
  46027. if ($params && isset($params[0]) && !isset($params[1])) {
  46028. $params = $params[0];
  46029. }
  46030. $stuff =
  46031. array(
  46032. $this->_pkg->getTasksNs().':id' => $id,
  46033. );
  46034. if ($instructions) {
  46035. $stuff[$this->_pkg->getTasksNs().':instructions'] = $instructions;
  46036. }
  46037. if ($params) {
  46038. $stuff[$this->_pkg->getTasksNs().':param'] = $params;
  46039. }
  46040. $this->_params[$this->_pkg->getTasksNs().':paramgroup'][] = $stuff;
  46041. }
  46042. /**
  46043. * Add a complex <paramgroup> to the post-install script with conditions
  46044. *
  46045. * This inserts a <paramgroup> with
  46046. *
  46047. * Order is significant, so call this method in the same
  46048. * sequence the users should see the paramgroups. The $params
  46049. * parameter should either be the result of a call to {@link getParam()}
  46050. * or an array of calls to getParam().
  46051. *
  46052. * Use {@link addParamGroup()} to add a simple <paramgroup>
  46053. *
  46054. * @param string $id <paramgroup> id as seen by the script
  46055. * @param string $oldgroup <paramgroup> id of the section referenced by
  46056. * <conditiontype>
  46057. * @param string $param name of the <param> from the older section referenced
  46058. * by <contitiontype>
  46059. * @param string $value value to match of the parameter
  46060. * @param string $conditiontype one of '=', '!=', 'preg_match'
  46061. * @param array|false $params array of getParam() calls, or false for no params
  46062. * @param string|false $instructions
  46063. */
  46064. public function addConditionTypeGroup($id,
  46065. $oldgroup,
  46066. $param,
  46067. $value,
  46068. $conditiontype = '=',
  46069. $params = false,
  46070. $instructions = false
  46071. ) {
  46072. if ($params && isset($params[0]) && !isset($params[1])) {
  46073. $params = $params[0];
  46074. }
  46075. $stuff = array(
  46076. $this->_pkg->getTasksNs().':id' => $id,
  46077. );
  46078. if ($instructions) {
  46079. $stuff[$this->_pkg->getTasksNs().':instructions'] = $instructions;
  46080. }
  46081. $stuff[$this->_pkg->getTasksNs().':name'] = $oldgroup.'::'.$param;
  46082. $stuff[$this->_pkg->getTasksNs().':conditiontype'] = $conditiontype;
  46083. $stuff[$this->_pkg->getTasksNs().':value'] = $value;
  46084. if ($params) {
  46085. $stuff[$this->_pkg->getTasksNs().':param'] = $params;
  46086. }
  46087. $this->_params[$this->_pkg->getTasksNs().':paramgroup'][] = $stuff;
  46088. }
  46089. public function getXml()
  46090. {
  46091. return $this->_params;
  46092. }
  46093. /**
  46094. * Use to set up a param tag for use in creating a paramgroup
  46095. *
  46096. * @param mixed $name Name of parameter
  46097. * @param mixed $prompt Prompt
  46098. * @param string $type Type, defaults to 'string'
  46099. * @param mixed $default Default value
  46100. *
  46101. * @return array
  46102. */
  46103. public function getParam(
  46104. $name, $prompt, $type = 'string', $default = null
  46105. ) {
  46106. if ($default !== null) {
  46107. return
  46108. array(
  46109. $this->_pkg->getTasksNs().':name' => $name,
  46110. $this->_pkg->getTasksNs().':prompt' => $prompt,
  46111. $this->_pkg->getTasksNs().':type' => $type,
  46112. $this->_pkg->getTasksNs().':default' => $default,
  46113. );
  46114. }
  46115. return
  46116. array(
  46117. $this->_pkg->getTasksNs().':name' => $name,
  46118. $this->_pkg->getTasksNs().':prompt' => $prompt,
  46119. $this->_pkg->getTasksNs().':type' => $type,
  46120. );
  46121. }
  46122. }
  46123. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Task/Replace/rw.php���������������������������������������������������������������0000644�0001750�0001750�00000003027�14720722517�016435� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  46124. /**
  46125. * <tasks:replace> - read/write version
  46126. *
  46127. * PHP versions 4 and 5
  46128. *
  46129. * @category pear
  46130. * @package PEAR
  46131. * @author Greg Beaver <cellog@php.net>
  46132. * @copyright 1997-2009 The Authors
  46133. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  46134. * @link http://pear.php.net/package/PEAR
  46135. * @since File available since Release 1.4.0a10
  46136. */
  46137. /**
  46138. * Base class
  46139. */
  46140. require_once 'PEAR/Task/Replace.php';
  46141. /**
  46142. * Abstracts the replace task xml.
  46143. * @category pear
  46144. * @package PEAR
  46145. * @author Greg Beaver <cellog@php.net>
  46146. * @copyright 1997-2009 The Authors
  46147. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  46148. * @version Release: 1.10.16
  46149. * @link http://pear.php.net/package/PEAR
  46150. * @since Class available since Release 1.4.0a10
  46151. */
  46152. class PEAR_Task_Replace_rw extends PEAR_Task_Replace
  46153. {
  46154. public function __construct(&$pkg, &$config, &$logger, $fileXml)
  46155. {
  46156. parent::__construct($config, $logger, PEAR_TASK_PACKAGE);
  46157. $this->_contents = $fileXml;
  46158. $this->_pkg = &$pkg;
  46159. $this->_params = array();
  46160. }
  46161. public function validate()
  46162. {
  46163. return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents);
  46164. }
  46165. public function setInfo($from, $to, $type)
  46166. {
  46167. $this->_params = array('attribs' => array('from' => $from, 'to' => $to, 'type' => $type));
  46168. }
  46169. public function getName()
  46170. {
  46171. return 'replace';
  46172. }
  46173. public function getXml()
  46174. {
  46175. return $this->_params;
  46176. }
  46177. }
  46178. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Task/Unixeol/rw.php���������������������������������������������������������������0000644�0001750�0001750�00000002434�14720722517�016506� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  46179. /**
  46180. * <tasks:unixeol> - read/write version
  46181. *
  46182. * PHP versions 4 and 5
  46183. *
  46184. * @category pear
  46185. * @package PEAR
  46186. * @author Greg Beaver <cellog@php.net>
  46187. * @copyright 1997-2009 The Authors
  46188. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  46189. * @link http://pear.php.net/package/PEAR
  46190. * @since File available since Release 1.4.0a10
  46191. */
  46192. /**
  46193. * Base class
  46194. */
  46195. require_once 'PEAR/Task/Unixeol.php';
  46196. /**
  46197. * Abstracts the unixeol task xml.
  46198. * @category pear
  46199. * @package PEAR
  46200. * @author Greg Beaver <cellog@php.net>
  46201. * @copyright 1997-2009 The Authors
  46202. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  46203. * @version Release: 1.10.16
  46204. * @link http://pear.php.net/package/PEAR
  46205. * @since Class available since Release 1.4.0a10
  46206. */
  46207. class PEAR_Task_Unixeol_rw extends PEAR_Task_Unixeol
  46208. {
  46209. function __construct(&$pkg, &$config, &$logger, $fileXml)
  46210. {
  46211. parent::__construct($config, $logger, PEAR_TASK_PACKAGE);
  46212. $this->_contents = $fileXml;
  46213. $this->_pkg = &$pkg;
  46214. $this->_params = array();
  46215. }
  46216. public function validate()
  46217. {
  46218. return true;
  46219. }
  46220. public function getName()
  46221. {
  46222. return 'unixeol';
  46223. }
  46224. public function getXml()
  46225. {
  46226. return '';
  46227. }
  46228. }
  46229. ?>
  46230. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Task/Windowseol/rw.php������������������������������������������������������������0000644�0001750�0001750�00000002451�14720722517�017214� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  46231. /**
  46232. * <tasks:windowseol> - read/write version
  46233. *
  46234. * PHP versions 4 and 5
  46235. *
  46236. * @category pear
  46237. * @package PEAR
  46238. * @author Greg Beaver <cellog@php.net>
  46239. * @copyright 1997-2009 The Authors
  46240. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  46241. * @link http://pear.php.net/package/PEAR
  46242. * @since File available since Release 1.4.0a10
  46243. */
  46244. /**
  46245. * Base class
  46246. */
  46247. require_once 'PEAR/Task/Windowseol.php';
  46248. /**
  46249. * Abstracts the windowseol task xml.
  46250. *
  46251. * @category pear
  46252. * @package PEAR
  46253. * @author Greg Beaver <cellog@php.net>
  46254. * @copyright 1997-2009 The Authors
  46255. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  46256. * @version Release: 1.10.16
  46257. * @link http://pear.php.net/package/PEAR
  46258. * @since Class available since Release 1.4.0a10
  46259. */
  46260. class PEAR_Task_Windowseol_rw extends PEAR_Task_Windowseol
  46261. {
  46262. function __construct(&$pkg, &$config, &$logger, $fileXml)
  46263. {
  46264. parent::__construct($config, $logger, PEAR_TASK_PACKAGE);
  46265. $this->_contents = $fileXml;
  46266. $this->_pkg = &$pkg;
  46267. $this->_params = array();
  46268. }
  46269. public function validate()
  46270. {
  46271. return true;
  46272. }
  46273. public function getName()
  46274. {
  46275. return 'windowseol';
  46276. }
  46277. public function getXml()
  46278. {
  46279. return '';
  46280. }
  46281. }
  46282. ?>
  46283. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Task/Common.php�������������������������������������������������������������������0000644�0001750�0001750�00000014037�14720722517�015665� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  46284. /**
  46285. * PEAR_Task_Common, base class for installer tasks
  46286. *
  46287. * PHP versions 4 and 5
  46288. *
  46289. * @category pear
  46290. * @package PEAR
  46291. * @author Greg Beaver <cellog@php.net>
  46292. * @copyright 1997-2009 The Authors
  46293. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  46294. * @link http://pear.php.net/package/PEAR
  46295. * @since File available since Release 1.4.0a1
  46296. */
  46297. /**#@+
  46298. * Error codes for task validation routines
  46299. */
  46300. define('PEAR_TASK_ERROR_NOATTRIBS', 1);
  46301. define('PEAR_TASK_ERROR_MISSING_ATTRIB', 2);
  46302. define('PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE', 3);
  46303. define('PEAR_TASK_ERROR_INVALID', 4);
  46304. /**#@-*/
  46305. define('PEAR_TASK_PACKAGE', 1);
  46306. define('PEAR_TASK_INSTALL', 2);
  46307. define('PEAR_TASK_PACKAGEANDINSTALL', 3);
  46308. /**
  46309. * A task is an operation that manipulates the contents of a file.
  46310. *
  46311. * Simple tasks operate on 1 file. Multiple tasks are executed after all files have been
  46312. * processed and installed, and are designed to operate on all files containing the task.
  46313. * The Post-install script task simply takes advantage of the fact that it will be run
  46314. * after installation, replace is a simple task.
  46315. *
  46316. * Combining tasks is possible, but ordering is significant.
  46317. *
  46318. * <file name="test.php" role="php">
  46319. * <tasks:replace from="@data-dir@" to="data_dir" type="pear-config"/>
  46320. * <tasks:postinstallscript/>
  46321. * </file>
  46322. *
  46323. * This will first replace any instance of @data-dir@ in the test.php file
  46324. * with the path to the current data directory. Then, it will include the
  46325. * test.php file and run the script it contains to configure the package post-installation.
  46326. *
  46327. * @category pear
  46328. * @package PEAR
  46329. * @author Greg Beaver <cellog@php.net>
  46330. * @copyright 1997-2009 The Authors
  46331. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  46332. * @version Release: 1.10.16
  46333. * @link http://pear.php.net/package/PEAR
  46334. * @since Class available since Release 1.4.0a1
  46335. * @abstract
  46336. */
  46337. class PEAR_Task_Common
  46338. {
  46339. /**
  46340. * Valid types for this version are 'simple' and 'multiple'
  46341. *
  46342. * - simple tasks operate on the contents of a file and write out changes to disk
  46343. * - multiple tasks operate on the contents of many files and write out the
  46344. * changes directly to disk
  46345. *
  46346. * Child task classes must override this property.
  46347. *
  46348. * @access protected
  46349. */
  46350. protected $type = 'simple';
  46351. /**
  46352. * Determines which install phase this task is executed under
  46353. */
  46354. public $phase = PEAR_TASK_INSTALL;
  46355. /**
  46356. * @access protected
  46357. */
  46358. protected $config;
  46359. /**
  46360. * @access protected
  46361. */
  46362. protected $registry;
  46363. /**
  46364. * @access protected
  46365. */
  46366. public $logger;
  46367. /**
  46368. * @access protected
  46369. */
  46370. protected $installphase;
  46371. /**
  46372. * @param PEAR_Config
  46373. * @param PEAR_Common
  46374. */
  46375. function __construct(&$config, &$logger, $phase)
  46376. {
  46377. $this->config = &$config;
  46378. $this->registry = &$config->getRegistry();
  46379. $this->logger = &$logger;
  46380. $this->installphase = $phase;
  46381. if ($this->type == 'multiple') {
  46382. $GLOBALS['_PEAR_TASK_POSTINSTANCES'][get_class($this)][] = &$this;
  46383. }
  46384. }
  46385. /**
  46386. * Validate the basic contents of a task tag.
  46387. *
  46388. * @param PEAR_PackageFile_v2
  46389. * @param array
  46390. * @param PEAR_Config
  46391. * @param array the entire parsed <file> tag
  46392. *
  46393. * @return true|array On error, return an array in format:
  46394. * array(PEAR_TASK_ERROR_???[, param1][, param2][, ...])
  46395. *
  46396. * For PEAR_TASK_ERROR_MISSING_ATTRIB, pass the attribute name in
  46397. * For PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, pass the attribute name and
  46398. * an array of legal values in
  46399. *
  46400. * @abstract
  46401. */
  46402. public static function validateXml($pkg, $xml, $config, $fileXml)
  46403. {
  46404. }
  46405. /**
  46406. * Initialize a task instance with the parameters
  46407. *
  46408. * @param array raw, parsed xml
  46409. * @param array attributes from the <file> tag containing this task
  46410. * @param string|null last installed version of this package
  46411. * @abstract
  46412. */
  46413. public function init($xml, $fileAttributes, $lastVersion)
  46414. {
  46415. }
  46416. /**
  46417. * Begin a task processing session. All multiple tasks will be processed
  46418. * after each file has been successfully installed, all simple tasks should
  46419. * perform their task here and return any errors using the custom
  46420. * throwError() method to allow forward compatibility
  46421. *
  46422. * This method MUST NOT write out any changes to disk
  46423. *
  46424. * @param PEAR_PackageFile_v2
  46425. * @param string file contents
  46426. * @param string the eventual final file location (informational only)
  46427. * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
  46428. * (use $this->throwError), otherwise return the new contents
  46429. * @abstract
  46430. */
  46431. public function startSession($pkg, $contents, $dest)
  46432. {
  46433. }
  46434. /**
  46435. * This method is used to process each of the tasks for a particular
  46436. * multiple class type. Simple tasks need not implement this method.
  46437. *
  46438. * @param array an array of tasks
  46439. * @access protected
  46440. */
  46441. public static function run($tasks)
  46442. {
  46443. }
  46444. /**
  46445. * @final
  46446. */
  46447. public static function hasPostinstallTasks()
  46448. {
  46449. return isset($GLOBALS['_PEAR_TASK_POSTINSTANCES']);
  46450. }
  46451. /**
  46452. * @final
  46453. */
  46454. public static function runPostinstallTasks()
  46455. {
  46456. foreach ($GLOBALS['_PEAR_TASK_POSTINSTANCES'] as $class => $tasks) {
  46457. $err = call_user_func(
  46458. array($class, 'run'),
  46459. $GLOBALS['_PEAR_TASK_POSTINSTANCES'][$class]
  46460. );
  46461. if ($err) {
  46462. return PEAR_Task_Common::throwError($err);
  46463. }
  46464. }
  46465. unset($GLOBALS['_PEAR_TASK_POSTINSTANCES']);
  46466. }
  46467. /**
  46468. * Determines whether a role is a script
  46469. * @return bool
  46470. */
  46471. public function isScript()
  46472. {
  46473. return $this->type == 'script';
  46474. }
  46475. public function throwError($msg, $code = -1)
  46476. {
  46477. include_once 'PEAR.php';
  46478. return PEAR::raiseError($msg, $code);
  46479. }
  46480. }
  46481. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Task/Postinstallscript.php��������������������������������������������������������0000644�0001750�0001750�00000034570�14720722517�020202� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  46482. /**
  46483. * <tasks:postinstallscript>
  46484. *
  46485. * PHP versions 4 and 5
  46486. *
  46487. * @category pear
  46488. * @package PEAR
  46489. * @author Greg Beaver <cellog@php.net>
  46490. * @copyright 1997-2009 The Authors
  46491. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  46492. * @link http://pear.php.net/package/PEAR
  46493. * @since File available since Release 1.4.0a1
  46494. */
  46495. /**
  46496. * Base class
  46497. */
  46498. require_once 'PEAR/Task/Common.php';
  46499. /**
  46500. * Implements the postinstallscript file task.
  46501. *
  46502. * Note that post-install scripts are handled separately from installation, by the
  46503. * "pear run-scripts" command
  46504. *
  46505. * @category pear
  46506. * @package PEAR
  46507. * @author Greg Beaver <cellog@php.net>
  46508. * @copyright 1997-2009 The Authors
  46509. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  46510. * @version Release: 1.10.16
  46511. * @link http://pear.php.net/package/PEAR
  46512. * @since Class available since Release 1.4.0a1
  46513. */
  46514. class PEAR_Task_Postinstallscript extends PEAR_Task_Common
  46515. {
  46516. public $type = 'script';
  46517. public $_class;
  46518. public $_params;
  46519. public $_obj;
  46520. /**
  46521. *
  46522. * @var PEAR_PackageFile_v2
  46523. */
  46524. public $_pkg;
  46525. public $_contents;
  46526. public $phase = PEAR_TASK_INSTALL;
  46527. /**
  46528. * Validate the raw xml at parsing-time.
  46529. *
  46530. * This also attempts to validate the script to make sure it meets the criteria
  46531. * for a post-install script
  46532. *
  46533. * @param PEAR_PackageFile_v2
  46534. * @param array The XML contents of the <postinstallscript> tag
  46535. * @param PEAR_Config
  46536. * @param array the entire parsed <file> tag
  46537. */
  46538. public static function validateXml($pkg, $xml, $config, $fileXml)
  46539. {
  46540. if ($fileXml['role'] != 'php') {
  46541. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  46542. $fileXml['name'].'" must be role="php"', );
  46543. }
  46544. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  46545. $file = $pkg->getFileContents($fileXml['name']);
  46546. if (PEAR::isError($file)) {
  46547. PEAR::popErrorHandling();
  46548. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  46549. $fileXml['name'].'" is not valid: '.
  46550. $file->getMessage(), );
  46551. } elseif ($file === null) {
  46552. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  46553. $fileXml['name'].'" could not be retrieved for processing!', );
  46554. } else {
  46555. $analysis = $pkg->analyzeSourceCode($file, true);
  46556. if (!$analysis) {
  46557. PEAR::popErrorHandling();
  46558. $warnings = '';
  46559. foreach ($pkg->getValidationWarnings() as $warn) {
  46560. $warnings .= $warn['message']."\n";
  46561. }
  46562. return array(PEAR_TASK_ERROR_INVALID, 'Analysis of post-install script "'.
  46563. $fileXml['name'].'" failed: '.$warnings, );
  46564. }
  46565. if (count($analysis['declared_classes']) != 1) {
  46566. PEAR::popErrorHandling();
  46567. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  46568. $fileXml['name'].'" must declare exactly 1 class', );
  46569. }
  46570. $class = $analysis['declared_classes'][0];
  46571. if ($class != str_replace(
  46572. array('/', '.php'), array('_', ''),
  46573. $fileXml['name']
  46574. ).'_postinstall') {
  46575. PEAR::popErrorHandling();
  46576. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  46577. $fileXml['name'].'" class "'.$class.'" must be named "'.
  46578. str_replace(
  46579. array('/', '.php'), array('_', ''),
  46580. $fileXml['name']
  46581. ).'_postinstall"', );
  46582. }
  46583. if (!isset($analysis['declared_methods'][$class])) {
  46584. PEAR::popErrorHandling();
  46585. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  46586. $fileXml['name'].'" must declare methods init() and run()', );
  46587. }
  46588. $methods = array('init' => 0, 'run' => 1);
  46589. foreach ($analysis['declared_methods'][$class] as $method) {
  46590. if (isset($methods[$method])) {
  46591. unset($methods[$method]);
  46592. }
  46593. }
  46594. if (count($methods)) {
  46595. PEAR::popErrorHandling();
  46596. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  46597. $fileXml['name'].'" must declare methods init() and run()', );
  46598. }
  46599. }
  46600. PEAR::popErrorHandling();
  46601. $definedparams = array();
  46602. $tasksNamespace = $pkg->getTasksNs().':';
  46603. if (!isset($xml[$tasksNamespace.'paramgroup']) && isset($xml['paramgroup'])) {
  46604. // in order to support the older betas, which did not expect internal tags
  46605. // to also use the namespace
  46606. $tasksNamespace = '';
  46607. }
  46608. if (isset($xml[$tasksNamespace.'paramgroup'])) {
  46609. $params = $xml[$tasksNamespace.'paramgroup'];
  46610. if (!is_array($params) || !isset($params[0])) {
  46611. $params = array($params);
  46612. }
  46613. foreach ($params as $param) {
  46614. if (!isset($param[$tasksNamespace.'id'])) {
  46615. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  46616. $fileXml['name'].'" <paramgroup> must have '.
  46617. 'an '.$tasksNamespace.'id> tag', );
  46618. }
  46619. if (isset($param[$tasksNamespace.'name'])) {
  46620. if (!in_array($param[$tasksNamespace.'name'], $definedparams)) {
  46621. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  46622. $fileXml['name'].'" '.$tasksNamespace.
  46623. 'paramgroup> id "'.$param[$tasksNamespace.'id'].
  46624. '" parameter "'.$param[$tasksNamespace.'name'].
  46625. '" has not been previously defined', );
  46626. }
  46627. if (!isset($param[$tasksNamespace.'conditiontype'])) {
  46628. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  46629. $fileXml['name'].'" '.$tasksNamespace.
  46630. 'paramgroup> id "'.$param[$tasksNamespace.'id'].
  46631. '" must have a '.$tasksNamespace.
  46632. 'conditiontype> tag containing either "=", '.
  46633. '"!=", or "preg_match"', );
  46634. }
  46635. if (!in_array(
  46636. $param[$tasksNamespace.'conditiontype'],
  46637. array('=', '!=', 'preg_match')
  46638. )) {
  46639. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  46640. $fileXml['name'].'" '.$tasksNamespace.
  46641. 'paramgroup> id "'.$param[$tasksNamespace.'id'].
  46642. '" must have a '.$tasksNamespace.
  46643. 'conditiontype> tag containing either "=", '.
  46644. '"!=", or "preg_match"', );
  46645. }
  46646. if (!isset($param[$tasksNamespace.'value'])) {
  46647. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  46648. $fileXml['name'].'" '.$tasksNamespace.
  46649. 'paramgroup> id "'.$param[$tasksNamespace.'id'].
  46650. '" must have a '.$tasksNamespace.
  46651. 'value> tag containing expected parameter value', );
  46652. }
  46653. }
  46654. if (isset($param[$tasksNamespace.'instructions'])) {
  46655. if (!is_string($param[$tasksNamespace.'instructions'])) {
  46656. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  46657. $fileXml['name'].'" '.$tasksNamespace.
  46658. 'paramgroup> id "'.$param[$tasksNamespace.'id'].
  46659. '" '.$tasksNamespace.'instructions> must be simple text', );
  46660. }
  46661. }
  46662. if (!isset($param[$tasksNamespace.'param'])) {
  46663. continue; // <param> is no longer required
  46664. }
  46665. $subparams = $param[$tasksNamespace.'param'];
  46666. if (!is_array($subparams) || !isset($subparams[0])) {
  46667. $subparams = array($subparams);
  46668. }
  46669. foreach ($subparams as $subparam) {
  46670. if (!isset($subparam[$tasksNamespace.'name'])) {
  46671. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  46672. $fileXml['name'].'" parameter for '.
  46673. $tasksNamespace.'paramgroup> id "'.
  46674. $param[$tasksNamespace.'id'].'" must have '.
  46675. 'a '.$tasksNamespace.'name> tag', );
  46676. }
  46677. if (!preg_match(
  46678. '/[a-zA-Z0-9]+/',
  46679. $subparam[$tasksNamespace.'name']
  46680. )) {
  46681. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  46682. $fileXml['name'].'" parameter "'.
  46683. $subparam[$tasksNamespace.'name'].
  46684. '" for '.$tasksNamespace.'paramgroup> id "'.
  46685. $param[$tasksNamespace.'id'].
  46686. '" is not a valid name. Must contain only alphanumeric characters', );
  46687. }
  46688. if (!isset($subparam[$tasksNamespace.'prompt'])) {
  46689. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  46690. $fileXml['name'].'" parameter "'.
  46691. $subparam[$tasksNamespace.'name'].
  46692. '" for '.$tasksNamespace.'paramgroup> id "'.
  46693. $param[$tasksNamespace.'id'].
  46694. '" must have a '.$tasksNamespace.'prompt> tag', );
  46695. }
  46696. if (!isset($subparam[$tasksNamespace.'type'])) {
  46697. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  46698. $fileXml['name'].'" parameter "'.
  46699. $subparam[$tasksNamespace.'name'].
  46700. '" for '.$tasksNamespace.'paramgroup> id "'.
  46701. $param[$tasksNamespace.'id'].
  46702. '" must have a '.$tasksNamespace.'type> tag', );
  46703. }
  46704. $definedparams[] = $param[$tasksNamespace.'id'].'::'.
  46705. $subparam[$tasksNamespace.'name'];
  46706. }
  46707. }
  46708. }
  46709. return true;
  46710. }
  46711. /**
  46712. * Initialize a task instance with the parameters
  46713. * @param array $xml raw, parsed xml
  46714. * @param array $fileattribs attributes from the <file> tag containing
  46715. * this task
  46716. * @param string|null $lastversion last installed version of this package,
  46717. * if any (useful for upgrades)
  46718. */
  46719. public function init($xml, $fileattribs, $lastversion)
  46720. {
  46721. $this->_class = str_replace('/', '_', $fileattribs['name']);
  46722. $this->_filename = $fileattribs['name'];
  46723. $this->_class = str_replace('.php', '', $this->_class).'_postinstall';
  46724. $this->_params = $xml;
  46725. $this->_lastversion = $lastversion;
  46726. }
  46727. /**
  46728. * Strip the tasks: namespace from internal params
  46729. *
  46730. * @access private
  46731. */
  46732. public function _stripNamespace($params = null)
  46733. {
  46734. if ($params === null) {
  46735. $params = array();
  46736. if (!is_array($this->_params)) {
  46737. return;
  46738. }
  46739. foreach ($this->_params as $i => $param) {
  46740. if (is_array($param)) {
  46741. $param = $this->_stripNamespace($param);
  46742. }
  46743. $params[str_replace($this->_pkg->getTasksNs().':', '', $i)] = $param;
  46744. }
  46745. $this->_params = $params;
  46746. } else {
  46747. $newparams = array();
  46748. foreach ($params as $i => $param) {
  46749. if (is_array($param)) {
  46750. $param = $this->_stripNamespace($param);
  46751. }
  46752. $newparams[str_replace($this->_pkg->getTasksNs().':', '', $i)] = $param;
  46753. }
  46754. return $newparams;
  46755. }
  46756. }
  46757. /**
  46758. * Unlike other tasks, the installed file name is passed in instead of the
  46759. * file contents, because this task is handled post-installation
  46760. *
  46761. * @param mixed $pkg PEAR_PackageFile_v1|PEAR_PackageFile_v2
  46762. * @param string $contents file name
  46763. * @param string $dest the eventual final file location (informational only)
  46764. *
  46765. * @return bool|PEAR_Error false to skip this file, PEAR_Error to fail
  46766. * (use $this->throwError)
  46767. */
  46768. public function startSession($pkg, $contents, $dest)
  46769. {
  46770. if ($this->installphase != PEAR_TASK_INSTALL) {
  46771. return false;
  46772. }
  46773. // remove the tasks: namespace if present
  46774. $this->_pkg = $pkg;
  46775. $this->_stripNamespace();
  46776. $this->logger->log(
  46777. 0, 'Including external post-installation script "'.
  46778. $contents.'" - any errors are in this script'
  46779. );
  46780. include_once $contents;
  46781. if (class_exists($this->_class)) {
  46782. $this->logger->log(0, 'Inclusion succeeded');
  46783. } else {
  46784. return $this->throwError(
  46785. 'init of post-install script class "'.$this->_class
  46786. .'" failed'
  46787. );
  46788. }
  46789. $this->_obj = new $this->_class();
  46790. $this->logger->log(1, 'running post-install script "'.$this->_class.'->init()"');
  46791. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  46792. $res = $this->_obj->init($this->config, $pkg, $this->_lastversion);
  46793. PEAR::popErrorHandling();
  46794. if ($res) {
  46795. $this->logger->log(0, 'init succeeded');
  46796. } else {
  46797. return $this->throwError(
  46798. 'init of post-install script "'.$this->_class.
  46799. '->init()" failed'
  46800. );
  46801. }
  46802. $this->_contents = $contents;
  46803. return true;
  46804. }
  46805. /**
  46806. * No longer used
  46807. *
  46808. * @see PEAR_PackageFile_v2::runPostinstallScripts()
  46809. * @param array an array of tasks
  46810. * @param string install or upgrade
  46811. * @access protected
  46812. */
  46813. public static function run($tasks)
  46814. {
  46815. }
  46816. }
  46817. ����������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Task/Replace.php������������������������������������������������������������������0000644�0001750�0001750�00000015371�14720722517�016012� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  46818. /**
  46819. * <tasks:replace>
  46820. *
  46821. * PHP versions 4 and 5
  46822. *
  46823. * @category pear
  46824. * @package PEAR
  46825. * @author Greg Beaver <cellog@php.net>
  46826. * @copyright 1997-2009 The Authors
  46827. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  46828. * @link http://pear.php.net/package/PEAR
  46829. * @since File available since Release 1.4.0a1
  46830. */
  46831. /**
  46832. * Base class
  46833. */
  46834. require_once 'PEAR/Task/Common.php';
  46835. /**
  46836. * Implements the replace file task.
  46837. * @category pear
  46838. * @package PEAR
  46839. * @author Greg Beaver <cellog@php.net>
  46840. * @copyright 1997-2009 The Authors
  46841. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  46842. * @version Release: 1.10.16
  46843. * @link http://pear.php.net/package/PEAR
  46844. * @since Class available since Release 1.4.0a1
  46845. */
  46846. class PEAR_Task_Replace extends PEAR_Task_Common
  46847. {
  46848. public $type = 'simple';
  46849. public $phase = PEAR_TASK_PACKAGEANDINSTALL;
  46850. public $_replacements;
  46851. /**
  46852. * Validate the raw xml at parsing-time.
  46853. *
  46854. * @param PEAR_PackageFile_v2
  46855. * @param array raw, parsed xml
  46856. * @param PEAR_Config
  46857. */
  46858. public static function validateXml($pkg, $xml, $config, $fileXml)
  46859. {
  46860. if (!isset($xml['attribs'])) {
  46861. return array(PEAR_TASK_ERROR_NOATTRIBS);
  46862. }
  46863. if (!isset($xml['attribs']['type'])) {
  46864. return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'type');
  46865. }
  46866. if (!isset($xml['attribs']['to'])) {
  46867. return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'to');
  46868. }
  46869. if (!isset($xml['attribs']['from'])) {
  46870. return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'from');
  46871. }
  46872. if ($xml['attribs']['type'] == 'pear-config') {
  46873. if (!in_array($xml['attribs']['to'], $config->getKeys())) {
  46874. return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
  46875. $config->getKeys(), );
  46876. }
  46877. } elseif ($xml['attribs']['type'] == 'php-const') {
  46878. if (defined($xml['attribs']['to'])) {
  46879. return true;
  46880. } else {
  46881. return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
  46882. array('valid PHP constant'), );
  46883. }
  46884. } elseif ($xml['attribs']['type'] == 'package-info') {
  46885. if (in_array(
  46886. $xml['attribs']['to'],
  46887. array('name', 'summary', 'channel', 'notes', 'extends', 'description',
  46888. 'release_notes', 'license', 'release-license', 'license-uri',
  46889. 'version', 'api-version', 'state', 'api-state', 'release_date',
  46890. 'date', 'time', )
  46891. )) {
  46892. return true;
  46893. } else {
  46894. return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
  46895. array('name', 'summary', 'channel', 'notes', 'extends', 'description',
  46896. 'release_notes', 'license', 'release-license', 'license-uri',
  46897. 'version', 'api-version', 'state', 'api-state', 'release_date',
  46898. 'date', 'time', ), );
  46899. }
  46900. } else {
  46901. return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'type', $xml['attribs']['type'],
  46902. array('pear-config', 'package-info', 'php-const'), );
  46903. }
  46904. return true;
  46905. }
  46906. /**
  46907. * Initialize a task instance with the parameters
  46908. * @param array raw, parsed xml
  46909. * @param unused
  46910. * @param unused
  46911. */
  46912. public function init($xml, $attribs, $lastVersion = null)
  46913. {
  46914. $this->_replacements = isset($xml['attribs']) ? array($xml) : $xml;
  46915. }
  46916. /**
  46917. * Do a package.xml 1.0 replacement, with additional package-info fields available
  46918. *
  46919. * See validateXml() source for the complete list of allowed fields
  46920. *
  46921. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  46922. * @param string file contents
  46923. * @param string the eventual final file location (informational only)
  46924. * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
  46925. * (use $this->throwError), otherwise return the new contents
  46926. */
  46927. public function startSession($pkg, $contents, $dest)
  46928. {
  46929. $subst_from = $subst_to = array();
  46930. foreach ($this->_replacements as $a) {
  46931. $a = $a['attribs'];
  46932. $to = '';
  46933. if ($a['type'] == 'pear-config') {
  46934. if ($this->installphase == PEAR_TASK_PACKAGE) {
  46935. return false;
  46936. }
  46937. if ($a['to'] == 'master_server') {
  46938. $chan = $this->registry->getChannel($pkg->getChannel());
  46939. if (!PEAR::isError($chan)) {
  46940. $to = $chan->getServer();
  46941. } else {
  46942. $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]");
  46943. return false;
  46944. }
  46945. } else {
  46946. if ($this->config->isDefinedLayer('ftp')) {
  46947. // try the remote config file first
  46948. $to = $this->config->get($a['to'], 'ftp', $pkg->getChannel());
  46949. if (is_null($to)) {
  46950. // then default to local
  46951. $to = $this->config->get($a['to'], null, $pkg->getChannel());
  46952. }
  46953. } else {
  46954. $to = $this->config->get($a['to'], null, $pkg->getChannel());
  46955. }
  46956. }
  46957. if (is_null($to)) {
  46958. $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]");
  46959. return false;
  46960. }
  46961. } elseif ($a['type'] == 'php-const') {
  46962. if ($this->installphase == PEAR_TASK_PACKAGE) {
  46963. return false;
  46964. }
  46965. if (defined($a['to'])) {
  46966. $to = constant($a['to']);
  46967. } else {
  46968. $this->logger->log(0, "$dest: invalid php-const replacement: $a[to]");
  46969. return false;
  46970. }
  46971. } else {
  46972. if ($t = $pkg->packageInfo($a['to'])) {
  46973. $to = $t;
  46974. } else {
  46975. $this->logger->log(0, "$dest: invalid package-info replacement: $a[to]");
  46976. return false;
  46977. }
  46978. }
  46979. if (!is_null($to)) {
  46980. $subst_from[] = $a['from'];
  46981. $subst_to[] = $to;
  46982. }
  46983. }
  46984. $this->logger->log(
  46985. 3, "doing ".sizeof($subst_from).
  46986. " substitution(s) for $dest"
  46987. );
  46988. if (sizeof($subst_from)) {
  46989. $contents = str_replace($subst_from, $subst_to, $contents);
  46990. }
  46991. return $contents;
  46992. }
  46993. }
  46994. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Task/Unixeol.php������������������������������������������������������������������0000644�0001750�0001750�00000004377�14720722517�016066� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  46995. /**
  46996. * <tasks:unixeol>
  46997. *
  46998. * PHP versions 4 and 5
  46999. *
  47000. * @category pear
  47001. * @package PEAR
  47002. * @author Greg Beaver <cellog@php.net>
  47003. * @copyright 1997-2009 The Authors
  47004. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  47005. * @link http://pear.php.net/package/PEAR
  47006. * @since File available since Release 1.4.0a1
  47007. */
  47008. /**
  47009. * Base class
  47010. */
  47011. require_once 'PEAR/Task/Common.php';
  47012. /**
  47013. * Implements the unix line endings file task.
  47014. * @category pear
  47015. * @package PEAR
  47016. * @author Greg Beaver <cellog@php.net>
  47017. * @copyright 1997-2009 The Authors
  47018. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  47019. * @version Release: 1.10.16
  47020. * @link http://pear.php.net/package/PEAR
  47021. * @since Class available since Release 1.4.0a1
  47022. */
  47023. class PEAR_Task_Unixeol extends PEAR_Task_Common
  47024. {
  47025. public $type = 'simple';
  47026. public $phase = PEAR_TASK_PACKAGE;
  47027. public $_replacements;
  47028. /**
  47029. * Validate the raw xml at parsing-time.
  47030. *
  47031. * @param PEAR_PackageFile_v2
  47032. * @param array raw, parsed xml
  47033. * @param PEAR_Config
  47034. */
  47035. public static function validateXml($pkg, $xml, $config, $fileXml)
  47036. {
  47037. if ($xml != '') {
  47038. return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed');
  47039. }
  47040. return true;
  47041. }
  47042. /**
  47043. * Initialize a task instance with the parameters
  47044. * @param array raw, parsed xml
  47045. * @param unused
  47046. * @param unused
  47047. */
  47048. public function init($xml, $attribs, $lastVersion = null)
  47049. {
  47050. }
  47051. /**
  47052. * Replace all line endings with line endings customized for the current OS
  47053. *
  47054. * See validateXml() source for the complete list of allowed fields
  47055. *
  47056. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  47057. * @param string file contents
  47058. * @param string the eventual final file location (informational only)
  47059. * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
  47060. * (use $this->throwError), otherwise return the new contents
  47061. */
  47062. public function startSession($pkg, $contents, $dest)
  47063. {
  47064. $this->logger->log(3, "replacing all line endings with \\n in $dest");
  47065. return preg_replace("/\r\n|\n\r|\r|\n/", "\n", $contents);
  47066. }
  47067. }
  47068. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Task/Windowseol.php���������������������������������������������������������������0000644�0001750�0001750�00000004363�14720722517�016570� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  47069. /**
  47070. * <tasks:windowseol>
  47071. *
  47072. * PHP versions 4 and 5
  47073. *
  47074. * @category pear
  47075. * @package PEAR
  47076. * @author Greg Beaver <cellog@php.net>
  47077. * @copyright 1997-2009 The Authors
  47078. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  47079. * @link http://pear.php.net/package/PEAR
  47080. * @since File available since Release 1.4.0a1
  47081. */
  47082. /**
  47083. * Base class
  47084. */
  47085. require_once 'PEAR/Task/Common.php';
  47086. /**
  47087. * Implements the windows line endsings file task.
  47088. *
  47089. * @category pear
  47090. * @package PEAR
  47091. * @author Greg Beaver <cellog@php.net>
  47092. * @copyright 1997-2009 The Authors
  47093. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  47094. * @version Release: 1.10.16
  47095. * @link http://pear.php.net/package/PEAR
  47096. * @since Class available since Release 1.4.0a1
  47097. */
  47098. class PEAR_Task_Windowseol extends PEAR_Task_Common
  47099. {
  47100. public $type = 'simple';
  47101. public $phase = PEAR_TASK_PACKAGE;
  47102. public $_replacements;
  47103. /**
  47104. * Validate the raw xml at parsing-time.
  47105. *
  47106. * @param PEAR_PackageFile_v2
  47107. * @param array raw, parsed xml
  47108. * @param PEAR_Config
  47109. */
  47110. public static function validateXml($pkg, $xml, $config, $fileXml)
  47111. {
  47112. if ($xml != '') {
  47113. return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed');
  47114. }
  47115. return true;
  47116. }
  47117. /**
  47118. * Initialize a task instance with the parameters
  47119. * @param array raw, parsed xml
  47120. * @param unused
  47121. * @param unused
  47122. */
  47123. public function init($xml, $attribs, $lastVersion = null)
  47124. {
  47125. }
  47126. /**
  47127. * Replace all line endings with windows line endings
  47128. *
  47129. * See validateXml() source for the complete list of allowed fields
  47130. *
  47131. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  47132. * @param string file contents
  47133. * @param string the eventual final file location (informational only)
  47134. * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
  47135. * (use $this->throwError), otherwise return the new contents
  47136. */
  47137. public function startSession($pkg, $contents, $dest)
  47138. {
  47139. $this->logger->log(3, "replacing all line endings with \\r\\n in $dest");
  47140. return preg_replace("/\r\n|\n\r|\r|\n/", "\r\n", $contents);
  47141. }
  47142. }
  47143. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Validator/PECL.php����������������������������������������������������������������0000644�0001750�0001750�00000004071�14720722517�016200� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  47144. /**
  47145. * Channel Validator for the pecl.php.net channel
  47146. *
  47147. * PHP 4 and PHP 5
  47148. *
  47149. * @category pear
  47150. * @package PEAR
  47151. * @author Greg Beaver <cellog@php.net>
  47152. * @copyright 1997-2006 The PHP Group
  47153. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  47154. * @link http://pear.php.net/package/PEAR
  47155. * @since File available since Release 1.4.0a5
  47156. */
  47157. /**
  47158. * This is the parent class for all validators
  47159. */
  47160. require_once 'PEAR/Validate.php';
  47161. /**
  47162. * Channel Validator for the pecl.php.net channel
  47163. * @category pear
  47164. * @package PEAR
  47165. * @author Greg Beaver <cellog@php.net>
  47166. * @copyright 1997-2009 The Authors
  47167. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  47168. * @version Release: 1.10.16
  47169. * @link http://pear.php.net/package/PEAR
  47170. * @since Class available since Release 1.4.0a5
  47171. */
  47172. class PEAR_Validator_PECL extends PEAR_Validate
  47173. {
  47174. function validateVersion()
  47175. {
  47176. if ($this->_state == PEAR_VALIDATE_PACKAGING) {
  47177. $version = $this->_packagexml->getVersion();
  47178. $versioncomponents = explode('.', $version);
  47179. $last = array_pop($versioncomponents);
  47180. if (substr($last, 1, 2) == 'rc') {
  47181. $this->_addFailure('version', 'Release Candidate versions must have ' .
  47182. 'upper-case RC, not lower-case rc');
  47183. return false;
  47184. }
  47185. }
  47186. return true;
  47187. }
  47188. function validatePackageName()
  47189. {
  47190. $ret = parent::validatePackageName();
  47191. if ($this->_packagexml->getPackageType() == 'extsrc' ||
  47192. $this->_packagexml->getPackageType() == 'zendextsrc') {
  47193. if (strtolower($this->_packagexml->getPackage()) !=
  47194. strtolower($this->_packagexml->getProvidesExtension())) {
  47195. $this->_addWarning('providesextension', 'package name "' .
  47196. $this->_packagexml->getPackage() . '" is different from extension name "' .
  47197. $this->_packagexml->getProvidesExtension() . '"');
  47198. }
  47199. }
  47200. return $ret;
  47201. }
  47202. }
  47203. ?>�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Builder.php�����������������������������������������������������������������������0000664�0001750�0001750�00000044773�14720722517�015135� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  47204. /**
  47205. * PEAR_Builder for building PHP extensions (PECL packages)
  47206. *
  47207. * PHP versions 4 and 5
  47208. *
  47209. * @category pear
  47210. * @package PEAR
  47211. * @author Stig Bakken <ssb@php.net>
  47212. * @author Greg Beaver <cellog@php.net>
  47213. * @copyright 1997-2009 The Authors
  47214. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  47215. * @link http://pear.php.net/package/PEAR
  47216. * @since File available since Release 0.1
  47217. *
  47218. * TODO: log output parameters in PECL command line
  47219. * TODO: msdev path in configuration
  47220. */
  47221. /**
  47222. * Needed for extending PEAR_Builder
  47223. */
  47224. require_once 'PEAR/Common.php';
  47225. require_once 'PEAR/PackageFile.php';
  47226. require_once 'System.php';
  47227. /**
  47228. * Class to handle building (compiling) extensions.
  47229. *
  47230. * @category pear
  47231. * @package PEAR
  47232. * @author Stig Bakken <ssb@php.net>
  47233. * @author Greg Beaver <cellog@php.net>
  47234. * @copyright 1997-2009 The Authors
  47235. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  47236. * @version Release: 1.10.16
  47237. * @link http://pear.php.net/package/PEAR
  47238. * @since Class available since PHP 4.0.2
  47239. * @see http://pear.php.net/manual/en/core.ppm.pear-builder.php
  47240. */
  47241. class PEAR_Builder extends PEAR_Common
  47242. {
  47243. var $php_api_version = 0;
  47244. var $zend_module_api_no = 0;
  47245. var $zend_extension_api_no = 0;
  47246. var $extensions_built = array();
  47247. /**
  47248. * @var string Used for reporting when it is not possible to pass function
  47249. * via extra parameter, e.g. log, msdevCallback
  47250. */
  47251. var $current_callback = null;
  47252. // used for msdev builds
  47253. var $_lastline = null;
  47254. var $_firstline = null;
  47255. /**
  47256. * Parsed --configureoptions.
  47257. *
  47258. * @var mixed[]
  47259. */
  47260. var $_parsed_configure_options;
  47261. /**
  47262. * PEAR_Builder constructor.
  47263. *
  47264. * @param mixed[] $configureoptions
  47265. * @param object $ui user interface object (instance of PEAR_Frontend_*)
  47266. *
  47267. * @access public
  47268. */
  47269. function __construct($configureoptions, &$ui)
  47270. {
  47271. parent::__construct();
  47272. $this->setFrontendObject($ui);
  47273. $this->_parseConfigureOptions($configureoptions);
  47274. }
  47275. /**
  47276. * Parse --configureoptions string.
  47277. *
  47278. * @param string Options, in the form "X=1 Y=2 Z='there\'s always one'"
  47279. */
  47280. function _parseConfigureOptions($options)
  47281. {
  47282. $data = '<XML><PROPERTIES ' . $options . ' /></XML>';
  47283. $parser = xml_parser_create('ISO-8859-1');
  47284. xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
  47285. xml_set_element_handler(
  47286. $parser, array($this, '_parseConfigureOptionsStartElement'),
  47287. array($this, '_parseConfigureOptionsEndElement'));
  47288. xml_parse($parser, $data, true);
  47289. xml_parser_free($parser);
  47290. }
  47291. /**
  47292. * Handle element start.
  47293. *
  47294. * @see PEAR_Builder::_parseConfigureOptions()
  47295. *
  47296. * @param resource $parser
  47297. * @param string $tagName
  47298. * @param mixed[] $attribs
  47299. */
  47300. function _parseConfigureOptionsStartElement($parser, $tagName, $attribs)
  47301. {
  47302. if ($tagName !== 'PROPERTIES') {
  47303. return;
  47304. }
  47305. $this->_parsed_configure_options = $attribs;
  47306. }
  47307. /**
  47308. * Handle element end.
  47309. *
  47310. * @see PEAR_Builder::_parseConfigureOptions()
  47311. *
  47312. * @param resource
  47313. * @param string $element
  47314. */
  47315. function _parseConfigureOptionsEndElement($parser, $element)
  47316. {
  47317. }
  47318. /**
  47319. * Build an extension from source on windows.
  47320. * requires msdev
  47321. */
  47322. function _build_win32($descfile, $callback = null)
  47323. {
  47324. if (is_object($descfile)) {
  47325. $pkg = $descfile;
  47326. $descfile = $pkg->getPackageFile();
  47327. } else {
  47328. $pf = new PEAR_PackageFile($this->config, $this->debug);
  47329. $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
  47330. if (PEAR::isError($pkg)) {
  47331. return $pkg;
  47332. }
  47333. }
  47334. $dir = dirname($descfile);
  47335. $old_cwd = getcwd();
  47336. if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) {
  47337. return $this->raiseError("could not chdir to $dir");
  47338. }
  47339. // packages that were in a .tar have the packagefile in this directory
  47340. $vdir = $pkg->getPackage() . '-' . $pkg->getVersion();
  47341. if (file_exists($dir) && is_dir($vdir)) {
  47342. if (!chdir($vdir)) {
  47343. return $this->raiseError("could not chdir to " . realpath($vdir));
  47344. }
  47345. $dir = getcwd();
  47346. }
  47347. $this->log(2, "building in $dir");
  47348. $dsp = $pkg->getPackage().'.dsp';
  47349. if (!file_exists("$dir/$dsp")) {
  47350. return $this->raiseError("The DSP $dsp does not exist.");
  47351. }
  47352. // XXX TODO: make release build type configurable
  47353. $command = 'msdev '.$dsp.' /MAKE "'.$pkg->getPackage(). ' - Release"';
  47354. $err = $this->_runCommand($command, array(&$this, 'msdevCallback'));
  47355. if (PEAR::isError($err)) {
  47356. return $err;
  47357. }
  47358. // figure out the build platform and type
  47359. $platform = 'Win32';
  47360. $buildtype = 'Release';
  47361. if (preg_match('/.*?'.$pkg->getPackage().'\s-\s(\w+)\s(.*?)-+/i',$this->_firstline,$matches)) {
  47362. $platform = $matches[1];
  47363. $buildtype = $matches[2];
  47364. }
  47365. if (preg_match('/(.*)?\s-\s(\d+).*?(\d+)/', $this->_lastline, $matches)) {
  47366. if ($matches[2]) {
  47367. // there were errors in the build
  47368. return $this->raiseError("There were errors during compilation.");
  47369. }
  47370. $out = $matches[1];
  47371. } else {
  47372. return $this->raiseError("Did not understand the completion status returned from msdev.exe.");
  47373. }
  47374. // msdev doesn't tell us the output directory :/
  47375. // open the dsp, find /out and use that directory
  47376. $dsptext = join('', file($dsp));
  47377. // this regex depends on the build platform and type having been
  47378. // correctly identified above.
  47379. $regex ='/.*?!IF\s+"\$\(CFG\)"\s+==\s+("'.
  47380. $pkg->getPackage().'\s-\s'.
  47381. $platform.'\s'.
  47382. $buildtype.'").*?'.
  47383. '\/out:"(.*?)"/is';
  47384. if ($dsptext && preg_match($regex, $dsptext, $matches)) {
  47385. // what we get back is a relative path to the output file itself.
  47386. $outfile = realpath($matches[2]);
  47387. } else {
  47388. return $this->raiseError("Could not retrieve output information from $dsp.");
  47389. }
  47390. // realpath returns false if the file doesn't exist
  47391. if ($outfile && copy($outfile, "$dir/$out")) {
  47392. $outfile = "$dir/$out";
  47393. }
  47394. $built_files[] = array(
  47395. 'file' => "$outfile",
  47396. 'php_api' => $this->php_api_version,
  47397. 'zend_mod_api' => $this->zend_module_api_no,
  47398. 'zend_ext_api' => $this->zend_extension_api_no,
  47399. );
  47400. return $built_files;
  47401. }
  47402. // }}}
  47403. // {{{ msdevCallback()
  47404. function msdevCallback($what, $data)
  47405. {
  47406. if (!$this->_firstline)
  47407. $this->_firstline = $data;
  47408. $this->_lastline = $data;
  47409. call_user_func($this->current_callback, $what, $data);
  47410. }
  47411. /**
  47412. * @param string
  47413. * @param string
  47414. * @param array
  47415. * @access private
  47416. */
  47417. function _harvestInstDir($dest_prefix, $dirname, &$built_files)
  47418. {
  47419. $d = opendir($dirname);
  47420. if (!$d)
  47421. return false;
  47422. $ret = true;
  47423. while (($ent = readdir($d)) !== false) {
  47424. if ($ent[0] == '.')
  47425. continue;
  47426. $full = $dirname . DIRECTORY_SEPARATOR . $ent;
  47427. if (is_dir($full)) {
  47428. if (!$this->_harvestInstDir(
  47429. $dest_prefix . DIRECTORY_SEPARATOR . $ent,
  47430. $full, $built_files)) {
  47431. $ret = false;
  47432. break;
  47433. }
  47434. } else {
  47435. $dest = $dest_prefix . DIRECTORY_SEPARATOR . $ent;
  47436. $built_files[] = array(
  47437. 'file' => $full,
  47438. 'dest' => $dest,
  47439. 'php_api' => $this->php_api_version,
  47440. 'zend_mod_api' => $this->zend_module_api_no,
  47441. 'zend_ext_api' => $this->zend_extension_api_no,
  47442. );
  47443. }
  47444. }
  47445. closedir($d);
  47446. return $ret;
  47447. }
  47448. /**
  47449. * Build an extension from source. Runs "phpize" in the source
  47450. * directory, but compiles in a temporary directory
  47451. * (TMPDIR/pear-build-USER/PACKAGE-VERSION).
  47452. *
  47453. * @param string|PEAR_PackageFile_v* $descfile path to XML package description file, or
  47454. * a PEAR_PackageFile object
  47455. *
  47456. * @param mixed $callback callback function used to report output,
  47457. * see PEAR_Builder::_runCommand for details
  47458. *
  47459. * @return array an array of associative arrays with built files,
  47460. * format:
  47461. * array( array( 'file' => '/path/to/ext.so',
  47462. * 'php_api' => YYYYMMDD,
  47463. * 'zend_mod_api' => YYYYMMDD,
  47464. * 'zend_ext_api' => YYYYMMDD ),
  47465. * ... )
  47466. *
  47467. * @access public
  47468. *
  47469. * @see PEAR_Builder::_runCommand
  47470. */
  47471. function build($descfile, $callback = null)
  47472. {
  47473. if (preg_match('/(\\/|\\\\|^)([^\\/\\\\]+)?php([^\\/\\\\]+)?$/',
  47474. $this->config->get('php_bin'), $matches)) {
  47475. if (isset($matches[2]) && strlen($matches[2]) &&
  47476. trim($matches[2]) != trim($this->config->get('php_prefix'))) {
  47477. $this->log(0, 'WARNING: php_bin ' . $this->config->get('php_bin') .
  47478. ' appears to have a prefix ' . $matches[2] . ', but' .
  47479. ' config variable php_prefix does not match');
  47480. }
  47481. if (isset($matches[3]) && strlen($matches[3]) &&
  47482. trim($matches[3]) != trim($this->config->get('php_suffix'))) {
  47483. $this->log(0, 'WARNING: php_bin ' . $this->config->get('php_bin') .
  47484. ' appears to have a suffix ' . $matches[3] . ', but' .
  47485. ' config variable php_suffix does not match');
  47486. }
  47487. }
  47488. $this->current_callback = $callback;
  47489. if (PEAR_OS == "Windows") {
  47490. return $this->_build_win32($descfile, $callback);
  47491. }
  47492. if (PEAR_OS != 'Unix') {
  47493. return $this->raiseError("building extensions not supported on this platform");
  47494. }
  47495. if (is_object($descfile)) {
  47496. $pkg = $descfile;
  47497. $descfile = $pkg->getPackageFile();
  47498. if (is_a($pkg, 'PEAR_PackageFile_v1')) {
  47499. $dir = dirname($descfile);
  47500. } else {
  47501. $dir = $pkg->_config->get('temp_dir') . '/' . $pkg->getName();
  47502. // automatically delete at session end
  47503. self::addTempFile($dir);
  47504. }
  47505. } else {
  47506. $pf = new PEAR_PackageFile($this->config);
  47507. $pkg = &$pf->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
  47508. if (PEAR::isError($pkg)) {
  47509. return $pkg;
  47510. }
  47511. $dir = dirname($descfile);
  47512. }
  47513. // Find config. outside of normal path - e.g. config.m4
  47514. foreach (array_keys($pkg->getInstallationFileList()) as $item) {
  47515. if (stristr(basename($item), 'config.m4') && dirname($item) != '.') {
  47516. $dir .= DIRECTORY_SEPARATOR . dirname($item);
  47517. break;
  47518. }
  47519. }
  47520. $old_cwd = getcwd();
  47521. if (!file_exists($dir) || !is_dir($dir) || !chdir($dir)) {
  47522. return $this->raiseError("could not chdir to $dir");
  47523. }
  47524. $vdir = $pkg->getPackage() . '-' . $pkg->getVersion();
  47525. if (is_dir($vdir)) {
  47526. chdir($vdir);
  47527. }
  47528. $dir = getcwd();
  47529. $this->log(2, "building in $dir");
  47530. $binDir = $this->config->get('bin_dir');
  47531. if (!preg_match('@(^|:)' . preg_quote($binDir, '@') . '(:|$)@', getenv('PATH'))) {
  47532. putenv('PATH=' . $binDir . ':' . getenv('PATH'));
  47533. }
  47534. $err = $this->_runCommand($this->config->get('php_prefix')
  47535. . "phpize" .
  47536. $this->config->get('php_suffix'),
  47537. array(&$this, 'phpizeCallback'));
  47538. if (PEAR::isError($err)) {
  47539. return $err;
  47540. }
  47541. if (!$err) {
  47542. return $this->raiseError("`phpize' failed");
  47543. }
  47544. // {{{ start of interactive part
  47545. $configure_command = "$dir/configure";
  47546. $phpConfigName = $this->config->get('php_prefix')
  47547. . 'php-config'
  47548. . $this->config->get('php_suffix');
  47549. $phpConfigPath = System::which($phpConfigName);
  47550. if ($phpConfigPath !== false) {
  47551. $configure_command .= ' --with-php-config='
  47552. . $phpConfigPath;
  47553. }
  47554. $configure_options = $pkg->getConfigureOptions();
  47555. if ($configure_options) {
  47556. foreach ($configure_options as $option) {
  47557. $default = array_key_exists('default', $option) ? $option['default'] : null;
  47558. if (array_key_exists($option['name'], $this->_parsed_configure_options)) {
  47559. $response = $this->_parsed_configure_options[$option['name']];
  47560. } else {
  47561. list($response) = $this->ui->userDialog(
  47562. 'build', [$option['prompt']], ['text'], [$default]);
  47563. }
  47564. if (substr($option['name'], 0, 5) === 'with-' &&
  47565. ($response === 'yes' || $response === 'autodetect')) {
  47566. $configure_command .= " --{$option['name']}";
  47567. } else {
  47568. $configure_command .= " --{$option['name']}=".trim($response);
  47569. }
  47570. }
  47571. }
  47572. // }}} end of interactive part
  47573. // FIXME make configurable
  47574. if (!$user=getenv('USER')) {
  47575. $user='defaultuser';
  47576. }
  47577. $tmpdir = $this->config->get('temp_dir');
  47578. $build_basedir = System::mktemp(' -t "' . $tmpdir . '" -d "pear-build-' . $user . '"');
  47579. $build_dir = "$build_basedir/$vdir";
  47580. $inst_dir = "$build_basedir/install-$vdir";
  47581. $this->log(1, "building in $build_dir");
  47582. if (is_dir($build_dir)) {
  47583. System::rm(array('-rf', $build_dir));
  47584. }
  47585. if (!System::mkDir(array('-p', $build_dir))) {
  47586. return $this->raiseError("could not create build dir: $build_dir");
  47587. }
  47588. self::addTempFile($build_dir);
  47589. if (!System::mkDir(array('-p', $inst_dir))) {
  47590. return $this->raiseError("could not create temporary install dir: $inst_dir");
  47591. }
  47592. self::addTempFile($inst_dir);
  47593. $make_command = getenv('MAKE') ? getenv('MAKE') : 'make';
  47594. $to_run = array(
  47595. $configure_command,
  47596. $make_command,
  47597. "$make_command INSTALL_ROOT=\"$inst_dir\" install",
  47598. "find \"$inst_dir\" | xargs ls -dils"
  47599. );
  47600. if (!file_exists($build_dir) || !is_dir($build_dir) || !chdir($build_dir)) {
  47601. return $this->raiseError("could not chdir to $build_dir");
  47602. }
  47603. putenv('PHP_PEAR_VERSION=1.10.16');
  47604. foreach ($to_run as $cmd) {
  47605. $err = $this->_runCommand($cmd, $callback);
  47606. if (PEAR::isError($err)) {
  47607. chdir($old_cwd);
  47608. return $err;
  47609. }
  47610. if (!$err) {
  47611. chdir($old_cwd);
  47612. return $this->raiseError("`$cmd' failed");
  47613. }
  47614. }
  47615. if (!($dp = opendir("modules"))) {
  47616. chdir($old_cwd);
  47617. return $this->raiseError("no `modules' directory found");
  47618. }
  47619. $built_files = array();
  47620. $prefix = exec($this->config->get('php_prefix')
  47621. . "php-config" .
  47622. $this->config->get('php_suffix') . " --prefix");
  47623. $this->_harvestInstDir($prefix, $inst_dir . DIRECTORY_SEPARATOR . $prefix, $built_files);
  47624. chdir($old_cwd);
  47625. return $built_files;
  47626. }
  47627. /**
  47628. * Message callback function used when running the "phpize"
  47629. * program. Extracts the API numbers used. Ignores other message
  47630. * types than "cmdoutput".
  47631. *
  47632. * @param string $what the type of message
  47633. * @param mixed $data the message
  47634. *
  47635. * @return void
  47636. *
  47637. * @access public
  47638. */
  47639. function phpizeCallback($what, $data)
  47640. {
  47641. if ($what != 'cmdoutput') {
  47642. return;
  47643. }
  47644. $this->log(1, rtrim($data));
  47645. if (preg_match('/You should update your .aclocal.m4/', $data)) {
  47646. return;
  47647. }
  47648. $matches = array();
  47649. if (preg_match('/^\s+(\S[^:]+):\s+(\d{8})/', $data, $matches)) {
  47650. $member = preg_replace('/[^a-z]/', '_', strtolower($matches[1]));
  47651. $apino = (int)$matches[2];
  47652. if (isset($this->$member)) {
  47653. $this->$member = $apino;
  47654. //$msg = sprintf("%-22s : %d", $matches[1], $apino);
  47655. //$this->log(1, $msg);
  47656. }
  47657. }
  47658. }
  47659. /**
  47660. * Run an external command, using a message callback to report
  47661. * output. The command will be run through popen and output is
  47662. * reported for every line with a "cmdoutput" message with the
  47663. * line string, including newlines, as payload.
  47664. *
  47665. * @param string $command the command to run
  47666. *
  47667. * @param mixed $callback (optional) function to use as message
  47668. * callback
  47669. *
  47670. * @return bool whether the command was successful (exit code 0
  47671. * means success, any other means failure)
  47672. *
  47673. * @access private
  47674. */
  47675. function _runCommand($command, $callback = null)
  47676. {
  47677. $this->log(1, "running: $command");
  47678. $pp = popen("$command 2>&1", "r");
  47679. if (!$pp) {
  47680. return $this->raiseError("failed to run `$command'");
  47681. }
  47682. if ($callback && $callback[0]->debug == 1) {
  47683. $olddbg = $callback[0]->debug;
  47684. $callback[0]->debug = 2;
  47685. }
  47686. while ($line = fgets($pp, 1024)) {
  47687. if ($callback) {
  47688. call_user_func($callback, 'cmdoutput', $line);
  47689. } else {
  47690. $this->log(2, rtrim($line));
  47691. }
  47692. }
  47693. if ($callback && isset($olddbg)) {
  47694. $callback[0]->debug = $olddbg;
  47695. }
  47696. $exitcode = is_resource($pp) ? pclose($pp) : -1;
  47697. return ($exitcode == 0);
  47698. }
  47699. function log($level, $msg, $append_crlf = true)
  47700. {
  47701. if ($this->current_callback) {
  47702. if ($this->debug >= $level) {
  47703. call_user_func($this->current_callback, 'output', $msg);
  47704. }
  47705. return;
  47706. }
  47707. return parent::log($level, $msg, $append_crlf);
  47708. }
  47709. }
  47710. �����PEAR-1.10.16/PEAR/ChannelFile.php�������������������������������������������������������������������0000644�0001750�0001750�00000143234�14720722517�015705� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  47711. /**
  47712. * PEAR_ChannelFile, the channel handling class
  47713. *
  47714. * PHP versions 4 and 5
  47715. *
  47716. * @category pear
  47717. * @package PEAR
  47718. * @author Greg Beaver <cellog@php.net>
  47719. * @copyright 1997-2009 The Authors
  47720. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  47721. * @link http://pear.php.net/package/PEAR
  47722. * @since File available since Release 1.4.0a1
  47723. */
  47724. /**
  47725. * Needed for error handling
  47726. */
  47727. require_once 'PEAR/ErrorStack.php';
  47728. require_once 'PEAR/XMLParser.php';
  47729. require_once 'PEAR/Common.php';
  47730. /**
  47731. * Error code if the channel.xml <channel> tag does not contain a valid version
  47732. */
  47733. define('PEAR_CHANNELFILE_ERROR_NO_VERSION', 1);
  47734. /**
  47735. * Error code if the channel.xml <channel> tag version is not supported (version 1.0 is the only supported version,
  47736. * currently
  47737. */
  47738. define('PEAR_CHANNELFILE_ERROR_INVALID_VERSION', 2);
  47739. /**
  47740. * Error code if parsing is attempted with no xml extension
  47741. */
  47742. define('PEAR_CHANNELFILE_ERROR_NO_XML_EXT', 3);
  47743. /**
  47744. * Error code if creating the xml parser resource fails
  47745. */
  47746. define('PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER', 4);
  47747. /**
  47748. * Error code used for all sax xml parsing errors
  47749. */
  47750. define('PEAR_CHANNELFILE_ERROR_PARSER_ERROR', 5);
  47751. /**#@+
  47752. * Validation errors
  47753. */
  47754. /**
  47755. * Error code when channel name is missing
  47756. */
  47757. define('PEAR_CHANNELFILE_ERROR_NO_NAME', 6);
  47758. /**
  47759. * Error code when channel name is invalid
  47760. */
  47761. define('PEAR_CHANNELFILE_ERROR_INVALID_NAME', 7);
  47762. /**
  47763. * Error code when channel summary is missing
  47764. */
  47765. define('PEAR_CHANNELFILE_ERROR_NO_SUMMARY', 8);
  47766. /**
  47767. * Error code when channel summary is multi-line
  47768. */
  47769. define('PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY', 9);
  47770. /**
  47771. * Error code when channel server is missing for protocol
  47772. */
  47773. define('PEAR_CHANNELFILE_ERROR_NO_HOST', 10);
  47774. /**
  47775. * Error code when channel server is invalid for protocol
  47776. */
  47777. define('PEAR_CHANNELFILE_ERROR_INVALID_HOST', 11);
  47778. /**
  47779. * Error code when a mirror name is invalid
  47780. */
  47781. define('PEAR_CHANNELFILE_ERROR_INVALID_MIRROR', 21);
  47782. /**
  47783. * Error code when a mirror type is invalid
  47784. */
  47785. define('PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE', 22);
  47786. /**
  47787. * Error code when an attempt is made to generate xml, but the parsed content is invalid
  47788. */
  47789. define('PEAR_CHANNELFILE_ERROR_INVALID', 23);
  47790. /**
  47791. * Error code when an empty package name validate regex is passed in
  47792. */
  47793. define('PEAR_CHANNELFILE_ERROR_EMPTY_REGEX', 24);
  47794. /**
  47795. * Error code when a <function> tag has no version
  47796. */
  47797. define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION', 25);
  47798. /**
  47799. * Error code when a <function> tag has no name
  47800. */
  47801. define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME', 26);
  47802. /**
  47803. * Error code when a <validatepackage> tag has no name
  47804. */
  47805. define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME', 27);
  47806. /**
  47807. * Error code when a <validatepackage> tag has no version attribute
  47808. */
  47809. define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION', 28);
  47810. /**
  47811. * Error code when a mirror does not exist but is called for in one of the set*
  47812. * methods.
  47813. */
  47814. define('PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND', 32);
  47815. /**
  47816. * Error code when a server port is not numeric
  47817. */
  47818. define('PEAR_CHANNELFILE_ERROR_INVALID_PORT', 33);
  47819. /**
  47820. * Error code when <static> contains no version attribute
  47821. */
  47822. define('PEAR_CHANNELFILE_ERROR_NO_STATICVERSION', 34);
  47823. /**
  47824. * Error code when <baseurl> contains no type attribute in a <rest> protocol definition
  47825. */
  47826. define('PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE', 35);
  47827. /**
  47828. * Error code when a mirror is defined and the channel.xml represents the __uri pseudo-channel
  47829. */
  47830. define('PEAR_CHANNELFILE_URI_CANT_MIRROR', 36);
  47831. /**
  47832. * Error code when ssl attribute is present and is not "yes"
  47833. */
  47834. define('PEAR_CHANNELFILE_ERROR_INVALID_SSL', 37);
  47835. /**#@-*/
  47836. /**
  47837. * Mirror types allowed. Currently only internet servers are recognized.
  47838. */
  47839. $GLOBALS['_PEAR_CHANNELS_MIRROR_TYPES'] = array('server');
  47840. /**
  47841. * The Channel handling class
  47842. *
  47843. * @category pear
  47844. * @package PEAR
  47845. * @author Greg Beaver <cellog@php.net>
  47846. * @copyright 1997-2009 The Authors
  47847. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  47848. * @version Release: 1.10.16
  47849. * @link http://pear.php.net/package/PEAR
  47850. * @since Class available since Release 1.4.0a1
  47851. */
  47852. class PEAR_ChannelFile
  47853. {
  47854. /**
  47855. * @access private
  47856. * @var PEAR_ErrorStack
  47857. * @access private
  47858. */
  47859. var $_stack;
  47860. /**
  47861. * Supported channel.xml versions, for parsing
  47862. * @var array
  47863. * @access private
  47864. */
  47865. var $_supportedVersions = array('1.0');
  47866. /**
  47867. * Parsed channel information
  47868. * @var array
  47869. * @access private
  47870. */
  47871. var $_channelInfo;
  47872. /**
  47873. * index into the subchannels array, used for parsing xml
  47874. * @var int
  47875. * @access private
  47876. */
  47877. var $_subchannelIndex;
  47878. /**
  47879. * index into the mirrors array, used for parsing xml
  47880. * @var int
  47881. * @access private
  47882. */
  47883. var $_mirrorIndex;
  47884. /**
  47885. * Flag used to determine the validity of parsed content
  47886. * @var boolean
  47887. * @access private
  47888. */
  47889. var $_isValid = false;
  47890. function __construct()
  47891. {
  47892. $this->_stack = new PEAR_ErrorStack('PEAR_ChannelFile');
  47893. $this->_stack->setErrorMessageTemplate($this->_getErrorMessage());
  47894. $this->_isValid = false;
  47895. }
  47896. /**
  47897. * @return array
  47898. * @access protected
  47899. */
  47900. function _getErrorMessage()
  47901. {
  47902. return
  47903. array(
  47904. PEAR_CHANNELFILE_ERROR_INVALID_VERSION =>
  47905. 'While parsing channel.xml, an invalid version number "%version% was passed in, expecting one of %versions%',
  47906. PEAR_CHANNELFILE_ERROR_NO_VERSION =>
  47907. 'No version number found in <channel> tag',
  47908. PEAR_CHANNELFILE_ERROR_NO_XML_EXT =>
  47909. '%error%',
  47910. PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER =>
  47911. 'Unable to create XML parser',
  47912. PEAR_CHANNELFILE_ERROR_PARSER_ERROR =>
  47913. '%error%',
  47914. PEAR_CHANNELFILE_ERROR_NO_NAME =>
  47915. 'Missing channel name',
  47916. PEAR_CHANNELFILE_ERROR_INVALID_NAME =>
  47917. 'Invalid channel %tag% "%name%"',
  47918. PEAR_CHANNELFILE_ERROR_NO_SUMMARY =>
  47919. 'Missing channel summary',
  47920. PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY =>
  47921. 'Channel summary should be on one line, but is multi-line',
  47922. PEAR_CHANNELFILE_ERROR_NO_HOST =>
  47923. 'Missing channel server for %type% server',
  47924. PEAR_CHANNELFILE_ERROR_INVALID_HOST =>
  47925. 'Server name "%server%" is invalid for %type% server',
  47926. PEAR_CHANNELFILE_ERROR_INVALID_MIRROR =>
  47927. 'Invalid mirror name "%name%", mirror type %type%',
  47928. PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE =>
  47929. 'Invalid mirror type "%type%"',
  47930. PEAR_CHANNELFILE_ERROR_INVALID =>
  47931. 'Cannot generate xml, contents are invalid',
  47932. PEAR_CHANNELFILE_ERROR_EMPTY_REGEX =>
  47933. 'packagenameregex cannot be empty',
  47934. PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION =>
  47935. '%parent% %protocol% function has no version',
  47936. PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME =>
  47937. '%parent% %protocol% function has no name',
  47938. PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE =>
  47939. '%parent% rest baseurl has no type',
  47940. PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME =>
  47941. 'Validation package has no name in <validatepackage> tag',
  47942. PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION =>
  47943. 'Validation package "%package%" has no version',
  47944. PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND =>
  47945. 'Mirror "%mirror%" does not exist',
  47946. PEAR_CHANNELFILE_ERROR_INVALID_PORT =>
  47947. 'Port "%port%" must be numeric',
  47948. PEAR_CHANNELFILE_ERROR_NO_STATICVERSION =>
  47949. '<static> tag must contain version attribute',
  47950. PEAR_CHANNELFILE_URI_CANT_MIRROR =>
  47951. 'The __uri pseudo-channel cannot have mirrors',
  47952. PEAR_CHANNELFILE_ERROR_INVALID_SSL =>
  47953. '%server% has invalid ssl attribute "%ssl%" can only be yes or not present',
  47954. );
  47955. }
  47956. /**
  47957. * @param string contents of package.xml file
  47958. * @return bool success of parsing
  47959. */
  47960. function fromXmlString($data)
  47961. {
  47962. if (preg_match('/<channel\s+version="([0-9]+\.[0-9]+)"/', $data, $channelversion)) {
  47963. if (!in_array($channelversion[1], $this->_supportedVersions)) {
  47964. $this->_stack->push(PEAR_CHANNELFILE_ERROR_INVALID_VERSION, 'error',
  47965. array('version' => $channelversion[1]));
  47966. return false;
  47967. }
  47968. $parser = new PEAR_XMLParser;
  47969. $result = $parser->parse($data);
  47970. if ($result !== true) {
  47971. if ($result->getCode() == 1) {
  47972. $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_XML_EXT, 'error',
  47973. array('error' => $result->getMessage()));
  47974. } else {
  47975. $this->_stack->push(PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER, 'error');
  47976. }
  47977. return false;
  47978. }
  47979. $this->_channelInfo = $parser->getData();
  47980. return true;
  47981. } else {
  47982. $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_VERSION, 'error', array('xml' => $data));
  47983. return false;
  47984. }
  47985. }
  47986. /**
  47987. * @return array
  47988. */
  47989. function toArray()
  47990. {
  47991. if (!$this->_isValid && !$this->validate()) {
  47992. return false;
  47993. }
  47994. return $this->_channelInfo;
  47995. }
  47996. /**
  47997. * @param array
  47998. *
  47999. * @return PEAR_ChannelFile|false false if invalid
  48000. */
  48001. public static function &fromArray(
  48002. $data, $compatibility = false, $stackClass = 'PEAR_ErrorStack'
  48003. ) {
  48004. $a = new PEAR_ChannelFile($compatibility, $stackClass);
  48005. $a->_fromArray($data);
  48006. if (!$a->validate()) {
  48007. $a = false;
  48008. return $a;
  48009. }
  48010. return $a;
  48011. }
  48012. /**
  48013. * Unlike {@link fromArray()} this does not do any validation
  48014. *
  48015. * @param array
  48016. *
  48017. * @return PEAR_ChannelFile
  48018. */
  48019. public static function &fromArrayWithErrors(
  48020. $data, $compatibility = false, $stackClass = 'PEAR_ErrorStack'
  48021. ) {
  48022. $a = new PEAR_ChannelFile($compatibility, $stackClass);
  48023. $a->_fromArray($data);
  48024. return $a;
  48025. }
  48026. /**
  48027. * @param array
  48028. * @access private
  48029. */
  48030. function _fromArray($data)
  48031. {
  48032. $this->_channelInfo = $data;
  48033. }
  48034. /**
  48035. * Wrapper to {@link PEAR_ErrorStack::getErrors()}
  48036. * @param boolean determines whether to purge the error stack after retrieving
  48037. * @return array
  48038. */
  48039. function getErrors($purge = false)
  48040. {
  48041. return $this->_stack->getErrors($purge);
  48042. }
  48043. /**
  48044. * Unindent given string (?)
  48045. *
  48046. * @param string $str The string that has to be unindented.
  48047. * @return string
  48048. * @access private
  48049. */
  48050. function _unIndent($str)
  48051. {
  48052. // remove leading newlines
  48053. $str = preg_replace('/^[\r\n]+/', '', $str);
  48054. // find whitespace at the beginning of the first line
  48055. $indent_len = strspn($str, " \t");
  48056. $indent = substr($str, 0, $indent_len);
  48057. $data = '';
  48058. // remove the same amount of whitespace from following lines
  48059. foreach (explode("\n", $str) as $line) {
  48060. if (substr($line, 0, $indent_len) == $indent) {
  48061. $data .= substr($line, $indent_len) . "\n";
  48062. }
  48063. }
  48064. return $data;
  48065. }
  48066. /**
  48067. * Parse a channel.xml file. Expects the name of
  48068. * a channel xml file as input.
  48069. *
  48070. * @param string $descfile name of channel xml file
  48071. * @return bool success of parsing
  48072. */
  48073. function fromXmlFile($descfile)
  48074. {
  48075. if (!file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) ||
  48076. (!$fp = fopen($descfile, 'r'))) {
  48077. require_once 'PEAR.php';
  48078. return PEAR::raiseError("Unable to open $descfile");
  48079. }
  48080. // read the whole thing so we only get one cdata callback
  48081. // for each block of cdata
  48082. fclose($fp);
  48083. $data = file_get_contents($descfile);
  48084. return $this->fromXmlString($data);
  48085. }
  48086. /**
  48087. * Parse channel information from different sources
  48088. *
  48089. * This method is able to extract information about a channel
  48090. * from an .xml file or a string
  48091. *
  48092. * @access public
  48093. * @param string Filename of the source or the source itself
  48094. * @return bool
  48095. */
  48096. function fromAny($info)
  48097. {
  48098. if (is_string($info) && file_exists($info) && strlen($info) < 255) {
  48099. $tmp = substr($info, -4);
  48100. if ($tmp == '.xml') {
  48101. $info = $this->fromXmlFile($info);
  48102. } else {
  48103. $fp = fopen($info, "r");
  48104. $test = fread($fp, 5);
  48105. fclose($fp);
  48106. if ($test == "<?xml") {
  48107. $info = $this->fromXmlFile($info);
  48108. }
  48109. }
  48110. if (PEAR::isError($info)) {
  48111. require_once 'PEAR.php';
  48112. return PEAR::raiseError($info);
  48113. }
  48114. }
  48115. if (is_string($info)) {
  48116. $info = $this->fromXmlString($info);
  48117. }
  48118. return $info;
  48119. }
  48120. /**
  48121. * Return an XML document based on previous parsing and modifications
  48122. *
  48123. * @return string XML data
  48124. *
  48125. * @access public
  48126. */
  48127. function toXml()
  48128. {
  48129. if (!$this->_isValid && !$this->validate()) {
  48130. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID);
  48131. return false;
  48132. }
  48133. if (!isset($this->_channelInfo['attribs']['version'])) {
  48134. $this->_channelInfo['attribs']['version'] = '1.0';
  48135. }
  48136. $channelInfo = $this->_channelInfo;
  48137. $ret = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
  48138. $ret .= "<channel version=\"" .
  48139. $channelInfo['attribs']['version'] . "\" xmlns=\"http://pear.php.net/channel-1.0\"
  48140. xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
  48141. xsi:schemaLocation=\"http://pear.php.net/dtd/channel-"
  48142. . $channelInfo['attribs']['version'] . " http://pear.php.net/dtd/channel-" .
  48143. $channelInfo['attribs']['version'] . ".xsd\">
  48144. <name>$channelInfo[name]</name>
  48145. <summary>" . htmlspecialchars($channelInfo['summary'])."</summary>
  48146. ";
  48147. if (isset($channelInfo['suggestedalias'])) {
  48148. $ret .= ' <suggestedalias>' . $channelInfo['suggestedalias'] . "</suggestedalias>\n";
  48149. }
  48150. if (isset($channelInfo['validatepackage'])) {
  48151. $ret .= ' <validatepackage version="' .
  48152. $channelInfo['validatepackage']['attribs']['version']. '">' .
  48153. htmlspecialchars($channelInfo['validatepackage']['_content']) .
  48154. "</validatepackage>\n";
  48155. }
  48156. $ret .= " <servers>\n";
  48157. $ret .= ' <primary';
  48158. if (isset($channelInfo['servers']['primary']['attribs']['ssl'])) {
  48159. $ret .= ' ssl="' . $channelInfo['servers']['primary']['attribs']['ssl'] . '"';
  48160. }
  48161. if (isset($channelInfo['servers']['primary']['attribs']['port'])) {
  48162. $ret .= ' port="' . $channelInfo['servers']['primary']['attribs']['port'] . '"';
  48163. }
  48164. $ret .= ">\n";
  48165. if (isset($channelInfo['servers']['primary']['rest'])) {
  48166. $ret .= $this->_makeRestXml($channelInfo['servers']['primary']['rest'], ' ');
  48167. }
  48168. $ret .= " </primary>\n";
  48169. if (isset($channelInfo['servers']['mirror'])) {
  48170. $ret .= $this->_makeMirrorsXml($channelInfo);
  48171. }
  48172. $ret .= " </servers>\n";
  48173. $ret .= "</channel>";
  48174. return str_replace("\r", "\n", str_replace("\r\n", "\n", $ret));
  48175. }
  48176. /**
  48177. * Generate the <rest> tag
  48178. * @access private
  48179. */
  48180. function _makeRestXml($info, $indent)
  48181. {
  48182. $ret = $indent . "<rest>\n";
  48183. if (isset($info['baseurl']) && !isset($info['baseurl'][0])) {
  48184. $info['baseurl'] = array($info['baseurl']);
  48185. }
  48186. if (isset($info['baseurl'])) {
  48187. foreach ($info['baseurl'] as $url) {
  48188. $ret .= "$indent <baseurl type=\"" . $url['attribs']['type'] . "\"";
  48189. $ret .= ">" . $url['_content'] . "</baseurl>\n";
  48190. }
  48191. }
  48192. $ret .= $indent . "</rest>\n";
  48193. return $ret;
  48194. }
  48195. /**
  48196. * Generate the <mirrors> tag
  48197. * @access private
  48198. */
  48199. function _makeMirrorsXml($channelInfo)
  48200. {
  48201. $ret = "";
  48202. if (!isset($channelInfo['servers']['mirror'][0])) {
  48203. $channelInfo['servers']['mirror'] = array($channelInfo['servers']['mirror']);
  48204. }
  48205. foreach ($channelInfo['servers']['mirror'] as $mirror) {
  48206. $ret .= ' <mirror host="' . $mirror['attribs']['host'] . '"';
  48207. if (isset($mirror['attribs']['port'])) {
  48208. $ret .= ' port="' . $mirror['attribs']['port'] . '"';
  48209. }
  48210. if (isset($mirror['attribs']['ssl'])) {
  48211. $ret .= ' ssl="' . $mirror['attribs']['ssl'] . '"';
  48212. }
  48213. $ret .= ">\n";
  48214. if (isset($mirror['rest'])) {
  48215. if (isset($mirror['rest'])) {
  48216. $ret .= $this->_makeRestXml($mirror['rest'], ' ');
  48217. }
  48218. $ret .= " </mirror>\n";
  48219. } else {
  48220. $ret .= "/>\n";
  48221. }
  48222. }
  48223. return $ret;
  48224. }
  48225. /**
  48226. * Generate the <functions> tag
  48227. * @access private
  48228. */
  48229. function _makeFunctionsXml($functions, $indent, $rest = false)
  48230. {
  48231. $ret = '';
  48232. if (!isset($functions[0])) {
  48233. $functions = array($functions);
  48234. }
  48235. foreach ($functions as $function) {
  48236. $ret .= "$indent<function version=\"" . $function['attribs']['version'] . "\"";
  48237. if ($rest) {
  48238. $ret .= ' uri="' . $function['attribs']['uri'] . '"';
  48239. }
  48240. $ret .= ">" . $function['_content'] . "</function>\n";
  48241. }
  48242. return $ret;
  48243. }
  48244. /**
  48245. * Validation error. Also marks the object contents as invalid
  48246. * @param error code
  48247. * @param array error information
  48248. * @access private
  48249. */
  48250. function _validateError($code, $params = array())
  48251. {
  48252. $this->_stack->push($code, 'error', $params);
  48253. $this->_isValid = false;
  48254. }
  48255. /**
  48256. * Validation warning. Does not mark the object contents invalid.
  48257. * @param error code
  48258. * @param array error information
  48259. * @access private
  48260. */
  48261. function _validateWarning($code, $params = array())
  48262. {
  48263. $this->_stack->push($code, 'warning', $params);
  48264. }
  48265. /**
  48266. * Validate parsed file.
  48267. *
  48268. * @access public
  48269. * @return boolean
  48270. */
  48271. function validate()
  48272. {
  48273. $this->_isValid = true;
  48274. $info = $this->_channelInfo;
  48275. if (empty($info['name'])) {
  48276. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_NAME);
  48277. } elseif (!$this->validChannelServer($info['name'])) {
  48278. if ($info['name'] != '__uri') {
  48279. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, array('tag' => 'name',
  48280. 'name' => $info['name']));
  48281. }
  48282. }
  48283. if (empty($info['summary'])) {
  48284. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
  48285. } elseif (strpos(trim($info['summary']), "\n") !== false) {
  48286. $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
  48287. array('summary' => $info['summary']));
  48288. }
  48289. if (isset($info['suggestedalias'])) {
  48290. if (!$this->validChannelServer($info['suggestedalias'])) {
  48291. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
  48292. array('tag' => 'suggestedalias', 'name' =>$info['suggestedalias']));
  48293. }
  48294. }
  48295. if (isset($info['localalias'])) {
  48296. if (!$this->validChannelServer($info['localalias'])) {
  48297. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
  48298. array('tag' => 'localalias', 'name' =>$info['localalias']));
  48299. }
  48300. }
  48301. if (isset($info['validatepackage'])) {
  48302. if (!isset($info['validatepackage']['_content'])) {
  48303. $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME);
  48304. }
  48305. if (!isset($info['validatepackage']['attribs']['version'])) {
  48306. $content = isset($info['validatepackage']['_content']) ?
  48307. $info['validatepackage']['_content'] :
  48308. null;
  48309. $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION,
  48310. array('package' => $content));
  48311. }
  48312. }
  48313. if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['port']) &&
  48314. !is_numeric($info['servers']['primary']['attribs']['port'])) {
  48315. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_PORT,
  48316. array('port' => $info['servers']['primary']['attribs']['port']));
  48317. }
  48318. if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['ssl']) &&
  48319. $info['servers']['primary']['attribs']['ssl'] != 'yes') {
  48320. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
  48321. array('ssl' => $info['servers']['primary']['attribs']['ssl'],
  48322. 'server' => $info['name']));
  48323. }
  48324. if (isset($info['servers']['primary']['rest']) &&
  48325. isset($info['servers']['primary']['rest']['baseurl'])) {
  48326. $this->_validateFunctions('rest', $info['servers']['primary']['rest']['baseurl']);
  48327. }
  48328. if (isset($info['servers']['mirror'])) {
  48329. if ($this->_channelInfo['name'] == '__uri') {
  48330. $this->_validateError(PEAR_CHANNELFILE_URI_CANT_MIRROR);
  48331. }
  48332. if (!isset($info['servers']['mirror'][0])) {
  48333. $info['servers']['mirror'] = array($info['servers']['mirror']);
  48334. }
  48335. foreach ($info['servers']['mirror'] as $mirror) {
  48336. if (!isset($mirror['attribs']['host'])) {
  48337. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_HOST,
  48338. array('type' => 'mirror'));
  48339. } elseif (!$this->validChannelServer($mirror['attribs']['host'])) {
  48340. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_HOST,
  48341. array('server' => $mirror['attribs']['host'], 'type' => 'mirror'));
  48342. }
  48343. if (isset($mirror['attribs']['ssl']) && $mirror['attribs']['ssl'] != 'yes') {
  48344. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
  48345. array('ssl' => $info['ssl'], 'server' => $mirror['attribs']['host']));
  48346. }
  48347. if (isset($mirror['rest'])) {
  48348. $this->_validateFunctions('rest', $mirror['rest']['baseurl'],
  48349. $mirror['attribs']['host']);
  48350. }
  48351. }
  48352. }
  48353. return $this->_isValid;
  48354. }
  48355. /**
  48356. * @param string rest - protocol name this function applies to
  48357. * @param array the functions
  48358. * @param string the name of the parent element (mirror name, for instance)
  48359. */
  48360. function _validateFunctions($protocol, $functions, $parent = '')
  48361. {
  48362. if (!isset($functions[0])) {
  48363. $functions = array($functions);
  48364. }
  48365. foreach ($functions as $function) {
  48366. if (!isset($function['_content']) || empty($function['_content'])) {
  48367. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME,
  48368. array('parent' => $parent, 'protocol' => $protocol));
  48369. }
  48370. if ($protocol == 'rest') {
  48371. if (!isset($function['attribs']['type']) ||
  48372. empty($function['attribs']['type'])) {
  48373. $this->_validateError(PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE,
  48374. array('parent' => $parent, 'protocol' => $protocol));
  48375. }
  48376. } else {
  48377. if (!isset($function['attribs']['version']) ||
  48378. empty($function['attribs']['version'])) {
  48379. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION,
  48380. array('parent' => $parent, 'protocol' => $protocol));
  48381. }
  48382. }
  48383. }
  48384. }
  48385. /**
  48386. * Test whether a string contains a valid channel server.
  48387. * @param string $ver the package version to test
  48388. * @return bool
  48389. */
  48390. function validChannelServer($server)
  48391. {
  48392. if ($server == '__uri') {
  48393. return true;
  48394. }
  48395. return (bool) preg_match(PEAR_CHANNELS_SERVER_PREG, $server);
  48396. }
  48397. /**
  48398. * @return string|false
  48399. */
  48400. function getName()
  48401. {
  48402. if (isset($this->_channelInfo['name'])) {
  48403. return $this->_channelInfo['name'];
  48404. }
  48405. return false;
  48406. }
  48407. /**
  48408. * @return string|false
  48409. */
  48410. function getServer()
  48411. {
  48412. if (isset($this->_channelInfo['name'])) {
  48413. return $this->_channelInfo['name'];
  48414. }
  48415. return false;
  48416. }
  48417. /**
  48418. * @return int|80 port number to connect to
  48419. */
  48420. function getPort($mirror = false)
  48421. {
  48422. if ($mirror) {
  48423. if ($mir = $this->getMirror($mirror)) {
  48424. if (isset($mir['attribs']['port'])) {
  48425. return $mir['attribs']['port'];
  48426. }
  48427. if ($this->getSSL($mirror)) {
  48428. return 443;
  48429. }
  48430. return 80;
  48431. }
  48432. return false;
  48433. }
  48434. if (isset($this->_channelInfo['servers']['primary']['attribs']['port'])) {
  48435. return $this->_channelInfo['servers']['primary']['attribs']['port'];
  48436. }
  48437. if ($this->getSSL()) {
  48438. return 443;
  48439. }
  48440. return 80;
  48441. }
  48442. /**
  48443. * @return bool Determines whether secure sockets layer (SSL) is used to connect to this channel
  48444. */
  48445. function getSSL($mirror = false)
  48446. {
  48447. if ($mirror) {
  48448. if ($mir = $this->getMirror($mirror)) {
  48449. if (isset($mir['attribs']['ssl'])) {
  48450. return true;
  48451. }
  48452. return false;
  48453. }
  48454. return false;
  48455. }
  48456. if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
  48457. return true;
  48458. }
  48459. return false;
  48460. }
  48461. /**
  48462. * @return string|false
  48463. */
  48464. function getSummary()
  48465. {
  48466. if (isset($this->_channelInfo['summary'])) {
  48467. return $this->_channelInfo['summary'];
  48468. }
  48469. return false;
  48470. }
  48471. /**
  48472. * @param string protocol type
  48473. * @param string Mirror name
  48474. * @return array|false
  48475. */
  48476. function getFunctions($protocol, $mirror = false)
  48477. {
  48478. if ($this->getName() == '__uri') {
  48479. return false;
  48480. }
  48481. $function = $protocol == 'rest' ? 'baseurl' : 'function';
  48482. if ($mirror) {
  48483. if ($mir = $this->getMirror($mirror)) {
  48484. if (isset($mir[$protocol][$function])) {
  48485. return $mir[$protocol][$function];
  48486. }
  48487. }
  48488. return false;
  48489. }
  48490. if (isset($this->_channelInfo['servers']['primary'][$protocol][$function])) {
  48491. return $this->_channelInfo['servers']['primary'][$protocol][$function];
  48492. }
  48493. return false;
  48494. }
  48495. /**
  48496. * @param string Protocol type
  48497. * @param string Function name (null to return the
  48498. * first protocol of the type requested)
  48499. * @param string Mirror name, if any
  48500. * @return array
  48501. */
  48502. function getFunction($type, $name = null, $mirror = false)
  48503. {
  48504. $protocols = $this->getFunctions($type, $mirror);
  48505. if (!$protocols) {
  48506. return false;
  48507. }
  48508. foreach ($protocols as $protocol) {
  48509. if ($name === null) {
  48510. return $protocol;
  48511. }
  48512. if ($protocol['_content'] != $name) {
  48513. continue;
  48514. }
  48515. return $protocol;
  48516. }
  48517. return false;
  48518. }
  48519. /**
  48520. * @param string protocol type
  48521. * @param string protocol name
  48522. * @param string version
  48523. * @param string mirror name
  48524. * @return boolean
  48525. */
  48526. function supports($type, $name = null, $mirror = false, $version = '1.0')
  48527. {
  48528. $protocols = $this->getFunctions($type, $mirror);
  48529. if (!$protocols) {
  48530. return false;
  48531. }
  48532. foreach ($protocols as $protocol) {
  48533. if ($protocol['attribs']['version'] != $version) {
  48534. continue;
  48535. }
  48536. if ($name === null) {
  48537. return true;
  48538. }
  48539. if ($protocol['_content'] != $name) {
  48540. continue;
  48541. }
  48542. return true;
  48543. }
  48544. return false;
  48545. }
  48546. /**
  48547. * Determines whether a channel supports Representational State Transfer (REST) protocols
  48548. * for retrieving channel information
  48549. * @param string
  48550. * @return bool
  48551. */
  48552. function supportsREST($mirror = false)
  48553. {
  48554. if ($mirror == $this->_channelInfo['name']) {
  48555. $mirror = false;
  48556. }
  48557. if ($mirror) {
  48558. if ($mir = $this->getMirror($mirror)) {
  48559. return isset($mir['rest']);
  48560. }
  48561. return false;
  48562. }
  48563. return isset($this->_channelInfo['servers']['primary']['rest']);
  48564. }
  48565. /**
  48566. * Get the URL to access a base resource.
  48567. *
  48568. * Hyperlinks in the returned xml will be used to retrieve the proper information
  48569. * needed. This allows extreme extensibility and flexibility in implementation
  48570. * @param string Resource Type to retrieve
  48571. */
  48572. function getBaseURL($resourceType, $mirror = false)
  48573. {
  48574. if ($mirror == $this->_channelInfo['name']) {
  48575. $mirror = false;
  48576. }
  48577. if ($mirror) {
  48578. $mir = $this->getMirror($mirror);
  48579. if (!$mir) {
  48580. return false;
  48581. }
  48582. $rest = $mir['rest'];
  48583. } else {
  48584. $rest = $this->_channelInfo['servers']['primary']['rest'];
  48585. }
  48586. if (!isset($rest['baseurl'][0])) {
  48587. $rest['baseurl'] = array($rest['baseurl']);
  48588. }
  48589. foreach ($rest['baseurl'] as $baseurl) {
  48590. if (strtolower($baseurl['attribs']['type']) == strtolower($resourceType)) {
  48591. return $baseurl['_content'];
  48592. }
  48593. }
  48594. return false;
  48595. }
  48596. /**
  48597. * Since REST does not implement RPC, provide this as a logical wrapper around
  48598. * resetFunctions for REST
  48599. * @param string|false mirror name, if any
  48600. */
  48601. function resetREST($mirror = false)
  48602. {
  48603. return $this->resetFunctions('rest', $mirror);
  48604. }
  48605. /**
  48606. * Empty all protocol definitions
  48607. * @param string protocol type
  48608. * @param string|false mirror name, if any
  48609. */
  48610. function resetFunctions($type, $mirror = false)
  48611. {
  48612. if ($mirror) {
  48613. if (isset($this->_channelInfo['servers']['mirror'])) {
  48614. $mirrors = $this->_channelInfo['servers']['mirror'];
  48615. if (!isset($mirrors[0])) {
  48616. $mirrors = array($mirrors);
  48617. }
  48618. foreach ($mirrors as $i => $mir) {
  48619. if ($mir['attribs']['host'] == $mirror) {
  48620. if (isset($this->_channelInfo['servers']['mirror'][$i][$type])) {
  48621. unset($this->_channelInfo['servers']['mirror'][$i][$type]);
  48622. }
  48623. return true;
  48624. }
  48625. }
  48626. return false;
  48627. }
  48628. return false;
  48629. }
  48630. if (isset($this->_channelInfo['servers']['primary'][$type])) {
  48631. unset($this->_channelInfo['servers']['primary'][$type]);
  48632. }
  48633. return true;
  48634. }
  48635. /**
  48636. * Set a channel's protocols to the protocols supported by pearweb
  48637. */
  48638. function setDefaultPEARProtocols($version = '1.0', $mirror = false)
  48639. {
  48640. switch ($version) {
  48641. case '1.0' :
  48642. $this->resetREST($mirror);
  48643. if (!isset($this->_channelInfo['servers'])) {
  48644. $this->_channelInfo['servers'] = array('primary' =>
  48645. array('rest' => array()));
  48646. } elseif (!isset($this->_channelInfo['servers']['primary'])) {
  48647. $this->_channelInfo['servers']['primary'] = array('rest' => array());
  48648. }
  48649. return true;
  48650. break;
  48651. default :
  48652. return false;
  48653. break;
  48654. }
  48655. }
  48656. /**
  48657. * @return array
  48658. */
  48659. function getMirrors()
  48660. {
  48661. if (isset($this->_channelInfo['servers']['mirror'])) {
  48662. $mirrors = $this->_channelInfo['servers']['mirror'];
  48663. if (!isset($mirrors[0])) {
  48664. $mirrors = array($mirrors);
  48665. }
  48666. return $mirrors;
  48667. }
  48668. return array();
  48669. }
  48670. /**
  48671. * Get the unserialized XML representing a mirror
  48672. * @return array|false
  48673. */
  48674. function getMirror($server)
  48675. {
  48676. foreach ($this->getMirrors() as $mirror) {
  48677. if ($mirror['attribs']['host'] == $server) {
  48678. return $mirror;
  48679. }
  48680. }
  48681. return false;
  48682. }
  48683. /**
  48684. * @param string
  48685. * @return string|false
  48686. * @error PEAR_CHANNELFILE_ERROR_NO_NAME
  48687. * @error PEAR_CHANNELFILE_ERROR_INVALID_NAME
  48688. */
  48689. function setName($name)
  48690. {
  48691. return $this->setServer($name);
  48692. }
  48693. /**
  48694. * Set the socket number (port) that is used to connect to this channel
  48695. * @param integer
  48696. * @param string|false name of the mirror server, or false for the primary
  48697. */
  48698. function setPort($port, $mirror = false)
  48699. {
  48700. if ($mirror) {
  48701. if (!isset($this->_channelInfo['servers']['mirror'])) {
  48702. $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  48703. array('mirror' => $mirror));
  48704. return false;
  48705. }
  48706. if (isset($this->_channelInfo['servers']['mirror'][0])) {
  48707. foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  48708. if ($mirror == $mir['attribs']['host']) {
  48709. $this->_channelInfo['servers']['mirror'][$i]['attribs']['port'] = $port;
  48710. return true;
  48711. }
  48712. }
  48713. return false;
  48714. } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
  48715. $this->_channelInfo['servers']['mirror']['attribs']['port'] = $port;
  48716. $this->_isValid = false;
  48717. return true;
  48718. }
  48719. }
  48720. $this->_channelInfo['servers']['primary']['attribs']['port'] = $port;
  48721. $this->_isValid = false;
  48722. return true;
  48723. }
  48724. /**
  48725. * Set the socket number (port) that is used to connect to this channel
  48726. * @param bool Determines whether to turn on SSL support or turn it off
  48727. * @param string|false name of the mirror server, or false for the primary
  48728. */
  48729. function setSSL($ssl = true, $mirror = false)
  48730. {
  48731. if ($mirror) {
  48732. if (!isset($this->_channelInfo['servers']['mirror'])) {
  48733. $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  48734. array('mirror' => $mirror));
  48735. return false;
  48736. }
  48737. if (isset($this->_channelInfo['servers']['mirror'][0])) {
  48738. foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  48739. if ($mirror == $mir['attribs']['host']) {
  48740. if (!$ssl) {
  48741. if (isset($this->_channelInfo['servers']['mirror'][$i]
  48742. ['attribs']['ssl'])) {
  48743. unset($this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl']);
  48744. }
  48745. } else {
  48746. $this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl'] = 'yes';
  48747. }
  48748. return true;
  48749. }
  48750. }
  48751. return false;
  48752. } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
  48753. if (!$ssl) {
  48754. if (isset($this->_channelInfo['servers']['mirror']['attribs']['ssl'])) {
  48755. unset($this->_channelInfo['servers']['mirror']['attribs']['ssl']);
  48756. }
  48757. } else {
  48758. $this->_channelInfo['servers']['mirror']['attribs']['ssl'] = 'yes';
  48759. }
  48760. $this->_isValid = false;
  48761. return true;
  48762. }
  48763. }
  48764. if ($ssl) {
  48765. $this->_channelInfo['servers']['primary']['attribs']['ssl'] = 'yes';
  48766. } else {
  48767. if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
  48768. unset($this->_channelInfo['servers']['primary']['attribs']['ssl']);
  48769. }
  48770. }
  48771. $this->_isValid = false;
  48772. return true;
  48773. }
  48774. /**
  48775. * @param string
  48776. * @return string|false
  48777. * @error PEAR_CHANNELFILE_ERROR_NO_SERVER
  48778. * @error PEAR_CHANNELFILE_ERROR_INVALID_SERVER
  48779. */
  48780. function setServer($server, $mirror = false)
  48781. {
  48782. if (empty($server)) {
  48783. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SERVER);
  48784. return false;
  48785. } elseif (!$this->validChannelServer($server)) {
  48786. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
  48787. array('tag' => 'name', 'name' => $server));
  48788. return false;
  48789. }
  48790. if ($mirror) {
  48791. $found = false;
  48792. foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  48793. if ($mirror == $mir['attribs']['host']) {
  48794. $found = true;
  48795. break;
  48796. }
  48797. }
  48798. if (!$found) {
  48799. $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  48800. array('mirror' => $mirror));
  48801. return false;
  48802. }
  48803. $this->_channelInfo['mirror'][$i]['attribs']['host'] = $server;
  48804. return true;
  48805. }
  48806. $this->_channelInfo['name'] = $server;
  48807. return true;
  48808. }
  48809. /**
  48810. * @param string
  48811. * @return boolean success
  48812. * @error PEAR_CHANNELFILE_ERROR_NO_SUMMARY
  48813. * @warning PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY
  48814. */
  48815. function setSummary($summary)
  48816. {
  48817. if (empty($summary)) {
  48818. $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
  48819. return false;
  48820. } elseif (strpos(trim($summary), "\n") !== false) {
  48821. $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
  48822. array('summary' => $summary));
  48823. }
  48824. $this->_channelInfo['summary'] = $summary;
  48825. return true;
  48826. }
  48827. /**
  48828. * @param string
  48829. * @param boolean determines whether the alias is in channel.xml or local
  48830. * @return boolean success
  48831. */
  48832. function setAlias($alias, $local = false)
  48833. {
  48834. if (!$this->validChannelServer($alias)) {
  48835. $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
  48836. array('tag' => 'suggestedalias', 'name' => $alias));
  48837. return false;
  48838. }
  48839. if ($local) {
  48840. $this->_channelInfo['localalias'] = $alias;
  48841. } else {
  48842. $this->_channelInfo['suggestedalias'] = $alias;
  48843. }
  48844. return true;
  48845. }
  48846. /**
  48847. * @return string
  48848. */
  48849. function getAlias()
  48850. {
  48851. if (isset($this->_channelInfo['localalias'])) {
  48852. return $this->_channelInfo['localalias'];
  48853. }
  48854. if (isset($this->_channelInfo['suggestedalias'])) {
  48855. return $this->_channelInfo['suggestedalias'];
  48856. }
  48857. if (isset($this->_channelInfo['name'])) {
  48858. return $this->_channelInfo['name'];
  48859. }
  48860. return '';
  48861. }
  48862. /**
  48863. * Set the package validation object if it differs from PEAR's default
  48864. * The class must be includeable via changing _ in the classname to path separator,
  48865. * but no checking of this is made.
  48866. * @param string|false pass in false to reset to the default packagename regex
  48867. * @return boolean success
  48868. */
  48869. function setValidationPackage($validateclass, $version)
  48870. {
  48871. if (empty($validateclass)) {
  48872. unset($this->_channelInfo['validatepackage']);
  48873. }
  48874. $this->_channelInfo['validatepackage'] = array('_content' => $validateclass);
  48875. $this->_channelInfo['validatepackage']['attribs'] = array('version' => $version);
  48876. }
  48877. /**
  48878. * Add a protocol to the provides section
  48879. * @param string protocol type
  48880. * @param string protocol version
  48881. * @param string protocol name, if any
  48882. * @param string mirror name, if this is a mirror's protocol
  48883. * @return bool
  48884. */
  48885. function addFunction($type, $version, $name = '', $mirror = false)
  48886. {
  48887. if ($mirror) {
  48888. return $this->addMirrorFunction($mirror, $type, $version, $name);
  48889. }
  48890. $set = array('attribs' => array('version' => $version), '_content' => $name);
  48891. if (!isset($this->_channelInfo['servers']['primary'][$type]['function'])) {
  48892. if (!isset($this->_channelInfo['servers'])) {
  48893. $this->_channelInfo['servers'] = array('primary' =>
  48894. array($type => array()));
  48895. } elseif (!isset($this->_channelInfo['servers']['primary'])) {
  48896. $this->_channelInfo['servers']['primary'] = array($type => array());
  48897. }
  48898. $this->_channelInfo['servers']['primary'][$type]['function'] = $set;
  48899. $this->_isValid = false;
  48900. return true;
  48901. } elseif (!isset($this->_channelInfo['servers']['primary'][$type]['function'][0])) {
  48902. $this->_channelInfo['servers']['primary'][$type]['function'] = array(
  48903. $this->_channelInfo['servers']['primary'][$type]['function']);
  48904. }
  48905. $this->_channelInfo['servers']['primary'][$type]['function'][] = $set;
  48906. return true;
  48907. }
  48908. /**
  48909. * Add a protocol to a mirror's provides section
  48910. * @param string mirror name (server)
  48911. * @param string protocol type
  48912. * @param string protocol version
  48913. * @param string protocol name, if any
  48914. */
  48915. function addMirrorFunction($mirror, $type, $version, $name = '')
  48916. {
  48917. if (!isset($this->_channelInfo['servers']['mirror'])) {
  48918. $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  48919. array('mirror' => $mirror));
  48920. return false;
  48921. }
  48922. $setmirror = false;
  48923. if (isset($this->_channelInfo['servers']['mirror'][0])) {
  48924. foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  48925. if ($mirror == $mir['attribs']['host']) {
  48926. $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
  48927. break;
  48928. }
  48929. }
  48930. } else {
  48931. if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
  48932. $setmirror = &$this->_channelInfo['servers']['mirror'];
  48933. }
  48934. }
  48935. if (!$setmirror) {
  48936. $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  48937. array('mirror' => $mirror));
  48938. return false;
  48939. }
  48940. $set = array('attribs' => array('version' => $version), '_content' => $name);
  48941. if (!isset($setmirror[$type]['function'])) {
  48942. $setmirror[$type]['function'] = $set;
  48943. $this->_isValid = false;
  48944. return true;
  48945. } elseif (!isset($setmirror[$type]['function'][0])) {
  48946. $setmirror[$type]['function'] = array($setmirror[$type]['function']);
  48947. }
  48948. $setmirror[$type]['function'][] = $set;
  48949. $this->_isValid = false;
  48950. return true;
  48951. }
  48952. /**
  48953. * @param string Resource Type this url links to
  48954. * @param string URL
  48955. * @param string|false mirror name, if this is not a primary server REST base URL
  48956. */
  48957. function setBaseURL($resourceType, $url, $mirror = false)
  48958. {
  48959. if ($mirror) {
  48960. if (!isset($this->_channelInfo['servers']['mirror'])) {
  48961. $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
  48962. array('mirror' => $mirror));
  48963. return false;
  48964. }
  48965. $setmirror = false;
  48966. if (isset($this->_channelInfo['servers']['mirror'][0])) {
  48967. foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
  48968. if ($mirror == $mir['attribs']['host']) {
  48969. $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
  48970. break;
  48971. }
  48972. }
  48973. } else {
  48974. if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
  48975. $setmirror = &$this->_channelInfo['servers']['mirror'];
  48976. }
  48977. }
  48978. } else {
  48979. $setmirror = &$this->_channelInfo['servers']['primary'];
  48980. }
  48981. $set = array('attribs' => array('type' => $resourceType), '_content' => $url);
  48982. if (!isset($setmirror['rest'])) {
  48983. $setmirror['rest'] = array();
  48984. }
  48985. if (!isset($setmirror['rest']['baseurl'])) {
  48986. $setmirror['rest']['baseurl'] = $set;
  48987. $this->_isValid = false;
  48988. return true;
  48989. } elseif (!isset($setmirror['rest']['baseurl'][0])) {
  48990. $setmirror['rest']['baseurl'] = array($setmirror['rest']['baseurl']);
  48991. }
  48992. foreach ($setmirror['rest']['baseurl'] as $i => $url) {
  48993. if ($url['attribs']['type'] == $resourceType) {
  48994. $this->_isValid = false;
  48995. $setmirror['rest']['baseurl'][$i] = $set;
  48996. return true;
  48997. }
  48998. }
  48999. $setmirror['rest']['baseurl'][] = $set;
  49000. $this->_isValid = false;
  49001. return true;
  49002. }
  49003. /**
  49004. * @param string mirror server
  49005. * @param int mirror http port
  49006. * @return boolean
  49007. */
  49008. function addMirror($server, $port = null)
  49009. {
  49010. if ($this->_channelInfo['name'] == '__uri') {
  49011. return false; // the __uri channel cannot have mirrors by definition
  49012. }
  49013. $set = array('attribs' => array('host' => $server));
  49014. if (is_numeric($port)) {
  49015. $set['attribs']['port'] = $port;
  49016. }
  49017. if (!isset($this->_channelInfo['servers']['mirror'])) {
  49018. $this->_channelInfo['servers']['mirror'] = $set;
  49019. return true;
  49020. }
  49021. if (!isset($this->_channelInfo['servers']['mirror'][0])) {
  49022. $this->_channelInfo['servers']['mirror'] =
  49023. array($this->_channelInfo['servers']['mirror']);
  49024. }
  49025. $this->_channelInfo['servers']['mirror'][] = $set;
  49026. return true;
  49027. }
  49028. /**
  49029. * Retrieve the name of the validation package for this channel
  49030. * @return string|false
  49031. */
  49032. function getValidationPackage()
  49033. {
  49034. if (!$this->_isValid && !$this->validate()) {
  49035. return false;
  49036. }
  49037. if (!isset($this->_channelInfo['validatepackage'])) {
  49038. return array('attribs' => array('version' => 'default'),
  49039. '_content' => 'PEAR_Validate');
  49040. }
  49041. return $this->_channelInfo['validatepackage'];
  49042. }
  49043. /**
  49044. * Retrieve the object that can be used for custom validation
  49045. * @param string|false the name of the package to validate. If the package is
  49046. * the channel validation package, PEAR_Validate is returned
  49047. * @return PEAR_Validate|false false is returned if the validation package
  49048. * cannot be located
  49049. */
  49050. function &getValidationObject($package = false)
  49051. {
  49052. if (!class_exists('PEAR_Validate')) {
  49053. require_once 'PEAR/Validate.php';
  49054. }
  49055. if (!$this->_isValid) {
  49056. if (!$this->validate()) {
  49057. $a = false;
  49058. return $a;
  49059. }
  49060. }
  49061. if (isset($this->_channelInfo['validatepackage'])) {
  49062. if ($package == $this->_channelInfo['validatepackage']) {
  49063. // channel validation packages are always validated by PEAR_Validate
  49064. $val = new PEAR_Validate;
  49065. return $val;
  49066. }
  49067. if (!class_exists(str_replace('.', '_',
  49068. $this->_channelInfo['validatepackage']['_content']))) {
  49069. if ($this->isIncludeable(str_replace('_', '/',
  49070. $this->_channelInfo['validatepackage']['_content']) . '.php')) {
  49071. include_once str_replace('_', '/',
  49072. $this->_channelInfo['validatepackage']['_content']) . '.php';
  49073. $vclass = str_replace('.', '_',
  49074. $this->_channelInfo['validatepackage']['_content']);
  49075. $val = new $vclass;
  49076. } else {
  49077. $a = false;
  49078. return $a;
  49079. }
  49080. } else {
  49081. $vclass = str_replace('.', '_',
  49082. $this->_channelInfo['validatepackage']['_content']);
  49083. $val = new $vclass;
  49084. }
  49085. } else {
  49086. $val = new PEAR_Validate;
  49087. }
  49088. return $val;
  49089. }
  49090. function isIncludeable($path)
  49091. {
  49092. $possibilities = explode(PATH_SEPARATOR, ini_get('include_path'));
  49093. foreach ($possibilities as $dir) {
  49094. if (file_exists($dir . DIRECTORY_SEPARATOR . $path)
  49095. && is_readable($dir . DIRECTORY_SEPARATOR . $path)) {
  49096. return true;
  49097. }
  49098. }
  49099. return false;
  49100. }
  49101. /**
  49102. * This function is used by the channel updater and retrieves a value set by
  49103. * the registry, or the current time if it has not been set
  49104. * @return string
  49105. */
  49106. function lastModified()
  49107. {
  49108. if (isset($this->_channelInfo['_lastmodified'])) {
  49109. return $this->_channelInfo['_lastmodified'];
  49110. }
  49111. return time();
  49112. }
  49113. }
  49114. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Command.php�����������������������������������������������������������������������0000664�0001750�0001750�00000030213�14720722517�015105� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  49115. /**
  49116. * PEAR_Command, command pattern class
  49117. *
  49118. * PHP versions 4 and 5
  49119. *
  49120. * @category pear
  49121. * @package PEAR
  49122. * @author Stig Bakken <ssb@php.net>
  49123. * @author Greg Beaver <cellog@php.net>
  49124. * @copyright 1997-2009 The Authors
  49125. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  49126. * @link http://pear.php.net/package/PEAR
  49127. * @since File available since Release 0.1
  49128. */
  49129. /**
  49130. * Needed for error handling
  49131. */
  49132. require_once 'PEAR.php';
  49133. require_once 'PEAR/Frontend.php';
  49134. require_once 'PEAR/XMLParser.php';
  49135. /**
  49136. * List of commands and what classes they are implemented in.
  49137. * @var array command => implementing class
  49138. */
  49139. $GLOBALS['_PEAR_Command_commandlist'] = array();
  49140. /**
  49141. * List of commands and their descriptions
  49142. * @var array command => description
  49143. */
  49144. $GLOBALS['_PEAR_Command_commanddesc'] = array();
  49145. /**
  49146. * List of shortcuts to common commands.
  49147. * @var array shortcut => command
  49148. */
  49149. $GLOBALS['_PEAR_Command_shortcuts'] = array();
  49150. /**
  49151. * Array of command objects
  49152. * @var array class => object
  49153. */
  49154. $GLOBALS['_PEAR_Command_objects'] = array();
  49155. /**
  49156. * PEAR command class, a simple factory class for administrative
  49157. * commands.
  49158. *
  49159. * How to implement command classes:
  49160. *
  49161. * - The class must be called PEAR_Command_Nnn, installed in the
  49162. * "PEAR/Common" subdir, with a method called getCommands() that
  49163. * returns an array of the commands implemented by the class (see
  49164. * PEAR/Command/Install.php for an example).
  49165. *
  49166. * - The class must implement a run() function that is called with three
  49167. * params:
  49168. *
  49169. * (string) command name
  49170. * (array) assoc array with options, freely defined by each
  49171. * command, for example:
  49172. * array('force' => true)
  49173. * (array) list of the other parameters
  49174. *
  49175. * The run() function returns a PEAR_CommandResponse object. Use
  49176. * these methods to get information:
  49177. *
  49178. * int getStatus() Returns PEAR_COMMAND_(SUCCESS|FAILURE|PARTIAL)
  49179. * *_PARTIAL means that you need to issue at least
  49180. * one more command to complete the operation
  49181. * (used for example for validation steps).
  49182. *
  49183. * string getMessage() Returns a message for the user. Remember,
  49184. * no HTML or other interface-specific markup.
  49185. *
  49186. * If something unexpected happens, run() returns a PEAR error.
  49187. *
  49188. * - DON'T OUTPUT ANYTHING! Return text for output instead.
  49189. *
  49190. * - DON'T USE HTML! The text you return will be used from both Gtk,
  49191. * web and command-line interfaces, so for now, keep everything to
  49192. * plain text.
  49193. *
  49194. * - DON'T USE EXIT OR DIE! Always use pear errors. From static
  49195. * classes do PEAR::raiseError(), from other classes do
  49196. * $this->raiseError().
  49197. * @category pear
  49198. * @package PEAR
  49199. * @author Stig Bakken <ssb@php.net>
  49200. * @author Greg Beaver <cellog@php.net>
  49201. * @copyright 1997-2009 The Authors
  49202. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  49203. * @version Release: 1.10.16
  49204. * @link http://pear.php.net/package/PEAR
  49205. * @since Class available since Release 0.1
  49206. */
  49207. class PEAR_Command
  49208. {
  49209. // {{{ factory()
  49210. /**
  49211. * Get the right object for executing a command.
  49212. *
  49213. * @param string $command The name of the command
  49214. * @param object $config Instance of PEAR_Config object
  49215. *
  49216. * @return object the command object or a PEAR error
  49217. */
  49218. public static function &factory($command, &$config)
  49219. {
  49220. if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
  49221. PEAR_Command::registerCommands();
  49222. }
  49223. if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
  49224. $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
  49225. }
  49226. if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
  49227. $a = PEAR::raiseError("unknown command `$command'");
  49228. return $a;
  49229. }
  49230. $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
  49231. if (!class_exists($class)) {
  49232. require_once $GLOBALS['_PEAR_Command_objects'][$class];
  49233. }
  49234. if (!class_exists($class)) {
  49235. $a = PEAR::raiseError("unknown command `$command'");
  49236. return $a;
  49237. }
  49238. $ui =& PEAR_Command::getFrontendObject();
  49239. $obj = new $class($ui, $config);
  49240. return $obj;
  49241. }
  49242. // }}}
  49243. // {{{ & getObject()
  49244. public static function &getObject($command)
  49245. {
  49246. $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
  49247. if (!class_exists($class)) {
  49248. require_once $GLOBALS['_PEAR_Command_objects'][$class];
  49249. }
  49250. if (!class_exists($class)) {
  49251. return PEAR::raiseError("unknown command `$command'");
  49252. }
  49253. $ui =& PEAR_Command::getFrontendObject();
  49254. $config = &PEAR_Config::singleton();
  49255. $obj = new $class($ui, $config);
  49256. return $obj;
  49257. }
  49258. // }}}
  49259. // {{{ & getFrontendObject()
  49260. /**
  49261. * Get instance of frontend object.
  49262. *
  49263. * @return object|PEAR_Error
  49264. */
  49265. public static function &getFrontendObject()
  49266. {
  49267. $a = &PEAR_Frontend::singleton();
  49268. return $a;
  49269. }
  49270. // }}}
  49271. // {{{ & setFrontendClass()
  49272. /**
  49273. * Load current frontend class.
  49274. *
  49275. * @param string $uiclass Name of class implementing the frontend
  49276. *
  49277. * @return object the frontend object, or a PEAR error
  49278. */
  49279. public static function &setFrontendClass($uiclass)
  49280. {
  49281. $a = &PEAR_Frontend::setFrontendClass($uiclass);
  49282. return $a;
  49283. }
  49284. // }}}
  49285. // {{{ setFrontendType()
  49286. /**
  49287. * Set current frontend.
  49288. *
  49289. * @param string $uitype Name of the frontend type (for example "CLI")
  49290. *
  49291. * @return object the frontend object, or a PEAR error
  49292. */
  49293. public static function setFrontendType($uitype)
  49294. {
  49295. $uiclass = 'PEAR_Frontend_' . $uitype;
  49296. return PEAR_Command::setFrontendClass($uiclass);
  49297. }
  49298. // }}}
  49299. // {{{ registerCommands()
  49300. /**
  49301. * Scan through the Command directory looking for classes
  49302. * and see what commands they implement.
  49303. *
  49304. * @param bool (optional) if FALSE (default), the new list of
  49305. * commands should replace the current one. If TRUE,
  49306. * new entries will be merged with old.
  49307. *
  49308. * @param string (optional) where (what directory) to look for
  49309. * classes, defaults to the Command subdirectory of
  49310. * the directory from where this file (__FILE__) is
  49311. * included.
  49312. *
  49313. * @return bool TRUE on success, a PEAR error on failure
  49314. */
  49315. public static function registerCommands($merge = false, $dir = null)
  49316. {
  49317. $parser = new PEAR_XMLParser;
  49318. if ($dir === null) {
  49319. $dir = dirname(__FILE__) . '/Command';
  49320. }
  49321. if (!is_dir($dir)) {
  49322. return PEAR::raiseError("registerCommands: opendir($dir) '$dir' does not exist or is not a directory");
  49323. }
  49324. $dp = @opendir($dir);
  49325. if (empty($dp)) {
  49326. return PEAR::raiseError("registerCommands: opendir($dir) failed");
  49327. }
  49328. if (!$merge) {
  49329. $GLOBALS['_PEAR_Command_commandlist'] = array();
  49330. }
  49331. while ($file = readdir($dp)) {
  49332. if ($file[0] == '.' || substr($file, -4) != '.xml') {
  49333. continue;
  49334. }
  49335. $f = substr($file, 0, -4);
  49336. $class = "PEAR_Command_" . $f;
  49337. // List of commands
  49338. if (empty($GLOBALS['_PEAR_Command_objects'][$class])) {
  49339. $GLOBALS['_PEAR_Command_objects'][$class] = "$dir/" . $f . '.php';
  49340. }
  49341. $parser->parse(file_get_contents("$dir/$file"));
  49342. $implements = $parser->getData();
  49343. foreach ($implements as $command => $desc) {
  49344. if ($command == 'attribs') {
  49345. continue;
  49346. }
  49347. if (isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
  49348. return PEAR::raiseError('Command "' . $command . '" already registered in ' .
  49349. 'class "' . $GLOBALS['_PEAR_Command_commandlist'][$command] . '"');
  49350. }
  49351. $GLOBALS['_PEAR_Command_commandlist'][$command] = $class;
  49352. $GLOBALS['_PEAR_Command_commanddesc'][$command] = $desc['summary'];
  49353. if (isset($desc['shortcut'])) {
  49354. $shortcut = $desc['shortcut'];
  49355. if (isset($GLOBALS['_PEAR_Command_shortcuts'][$shortcut])) {
  49356. return PEAR::raiseError('Command shortcut "' . $shortcut . '" already ' .
  49357. 'registered to command "' . $command . '" in class "' .
  49358. $GLOBALS['_PEAR_Command_commandlist'][$command] . '"');
  49359. }
  49360. $GLOBALS['_PEAR_Command_shortcuts'][$shortcut] = $command;
  49361. }
  49362. if (isset($desc['options']) && $desc['options']) {
  49363. foreach ($desc['options'] as $oname => $option) {
  49364. if (isset($option['shortopt']) && strlen($option['shortopt']) > 1) {
  49365. return PEAR::raiseError('Option "' . $oname . '" short option "' .
  49366. $option['shortopt'] . '" must be ' .
  49367. 'only 1 character in Command "' . $command . '" in class "' .
  49368. $class . '"');
  49369. }
  49370. }
  49371. }
  49372. }
  49373. }
  49374. ksort($GLOBALS['_PEAR_Command_shortcuts']);
  49375. ksort($GLOBALS['_PEAR_Command_commandlist']);
  49376. @closedir($dp);
  49377. return true;
  49378. }
  49379. // }}}
  49380. // {{{ getCommands()
  49381. /**
  49382. * Get the list of currently supported commands, and what
  49383. * classes implement them.
  49384. *
  49385. * @return array command => implementing class
  49386. */
  49387. public static function getCommands()
  49388. {
  49389. if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
  49390. PEAR_Command::registerCommands();
  49391. }
  49392. return $GLOBALS['_PEAR_Command_commandlist'];
  49393. }
  49394. // }}}
  49395. // {{{ getShortcuts()
  49396. /**
  49397. * Get the list of command shortcuts.
  49398. *
  49399. * @return array shortcut => command
  49400. */
  49401. public static function getShortcuts()
  49402. {
  49403. if (empty($GLOBALS['_PEAR_Command_shortcuts'])) {
  49404. PEAR_Command::registerCommands();
  49405. }
  49406. return $GLOBALS['_PEAR_Command_shortcuts'];
  49407. }
  49408. // }}}
  49409. // {{{ getGetoptArgs()
  49410. /**
  49411. * Compiles arguments for getopt.
  49412. *
  49413. * @param string $command command to get optstring for
  49414. * @param string $short_args (reference) short getopt format
  49415. * @param array $long_args (reference) long getopt format
  49416. *
  49417. * @return void
  49418. */
  49419. public static function getGetoptArgs($command, &$short_args, &$long_args)
  49420. {
  49421. if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
  49422. PEAR_Command::registerCommands();
  49423. }
  49424. if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
  49425. $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
  49426. }
  49427. if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
  49428. return null;
  49429. }
  49430. $obj = &PEAR_Command::getObject($command);
  49431. return $obj->getGetoptArgs($command, $short_args, $long_args);
  49432. }
  49433. // }}}
  49434. // {{{ getDescription()
  49435. /**
  49436. * Get description for a command.
  49437. *
  49438. * @param string $command Name of the command
  49439. *
  49440. * @return string command description
  49441. */
  49442. public static function getDescription($command)
  49443. {
  49444. if (!isset($GLOBALS['_PEAR_Command_commanddesc'][$command])) {
  49445. return null;
  49446. }
  49447. return $GLOBALS['_PEAR_Command_commanddesc'][$command];
  49448. }
  49449. // }}}
  49450. // {{{ getHelp()
  49451. /**
  49452. * Get help for command.
  49453. *
  49454. * @param string $command Name of the command to return help for
  49455. */
  49456. public static function getHelp($command)
  49457. {
  49458. $cmds = PEAR_Command::getCommands();
  49459. if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
  49460. $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
  49461. }
  49462. if (isset($cmds[$command])) {
  49463. $obj = &PEAR_Command::getObject($command);
  49464. return $obj->getHelp($command);
  49465. }
  49466. return false;
  49467. }
  49468. // }}}
  49469. }�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Common.php������������������������������������������������������������������������0000664�0001750�0001750�00000063542�14720722517�014772� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  49470. /**
  49471. * PEAR_Common, the base class for the PEAR Installer
  49472. *
  49473. * PHP versions 4 and 5
  49474. *
  49475. * @category pear
  49476. * @package PEAR
  49477. * @author Stig Bakken <ssb@php.net>
  49478. * @author Tomas V. V. Cox <cox@idecnet.com>
  49479. * @author Greg Beaver <cellog@php.net>
  49480. * @copyright 1997-2009 The Authors
  49481. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  49482. * @link http://pear.php.net/package/PEAR
  49483. * @since File available since Release 0.1.0
  49484. * @deprecated File deprecated since Release 1.4.0a1
  49485. */
  49486. /**
  49487. * Include error handling
  49488. */
  49489. require_once 'PEAR.php';
  49490. /**
  49491. * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode()
  49492. */
  49493. define('PEAR_COMMON_ERROR_INVALIDPHP', 1);
  49494. define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+');
  49495. define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/');
  49496. // this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1
  49497. define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?');
  49498. define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '\\z/i');
  49499. // XXX far from perfect :-)
  49500. define('_PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '(' . _PEAR_COMMON_PACKAGE_NAME_PREG .
  49501. ')(-([.0-9a-zA-Z]+))?');
  49502. define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_PACKAGE_DOWNLOAD_PREG .
  49503. '\\z/');
  49504. define('_PEAR_CHANNELS_NAME_PREG', '[A-Za-z][a-zA-Z0-9\.]+');
  49505. define('PEAR_CHANNELS_NAME_PREG', '/^' . _PEAR_CHANNELS_NAME_PREG . '\\z/');
  49506. // this should allow any dns or IP address, plus a path - NO UNDERSCORES ALLOWED
  49507. define('_PEAR_CHANNELS_SERVER_PREG', '[a-zA-Z0-9\-]+(?:\.[a-zA-Z0-9\-]+)*(\/[a-zA-Z0-9\-]+)*');
  49508. define('PEAR_CHANNELS_SERVER_PREG', '/^' . _PEAR_CHANNELS_SERVER_PREG . '\\z/i');
  49509. define('_PEAR_CHANNELS_PACKAGE_PREG', '(' ._PEAR_CHANNELS_SERVER_PREG . ')\/('
  49510. . _PEAR_COMMON_PACKAGE_NAME_PREG . ')');
  49511. define('PEAR_CHANNELS_PACKAGE_PREG', '/^' . _PEAR_CHANNELS_PACKAGE_PREG . '\\z/i');
  49512. define('_PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '(' . _PEAR_CHANNELS_NAME_PREG . ')::('
  49513. . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?');
  49514. define('PEAR_COMMON_CHANNEL_DOWNLOAD_PREG', '/^' . _PEAR_COMMON_CHANNEL_DOWNLOAD_PREG . '\\z/');
  49515. /**
  49516. * List of temporary files and directories registered by
  49517. * PEAR_Common::addTempFile().
  49518. * @var array
  49519. */
  49520. $GLOBALS['_PEAR_Common_tempfiles'] = array();
  49521. /**
  49522. * Valid maintainer roles
  49523. * @var array
  49524. */
  49525. $GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper');
  49526. /**
  49527. * Valid release states
  49528. * @var array
  49529. */
  49530. $GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel');
  49531. /**
  49532. * Valid dependency types
  49533. * @var array
  49534. */
  49535. $GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi');
  49536. /**
  49537. * Valid dependency relations
  49538. * @var array
  49539. */
  49540. $GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not', 'ne');
  49541. /**
  49542. * Valid file roles
  49543. * @var array
  49544. */
  49545. $GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script');
  49546. /**
  49547. * Valid replacement types
  49548. * @var array
  49549. */
  49550. $GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info');
  49551. /**
  49552. * Valid "provide" types
  49553. * @var array
  49554. */
  49555. $GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api');
  49556. /**
  49557. * Valid "provide" types
  49558. * @var array
  49559. */
  49560. $GLOBALS['_PEAR_Common_script_phases'] = array('pre-install', 'post-install', 'pre-uninstall', 'post-uninstall', 'pre-build', 'post-build', 'pre-configure', 'post-configure', 'pre-setup', 'post-setup');
  49561. /**
  49562. * Class providing common functionality for PEAR administration classes.
  49563. * @category pear
  49564. * @package PEAR
  49565. * @author Stig Bakken <ssb@php.net>
  49566. * @author Tomas V. V. Cox <cox@idecnet.com>
  49567. * @author Greg Beaver <cellog@php.net>
  49568. * @copyright 1997-2009 The Authors
  49569. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  49570. * @version Release: 1.10.16
  49571. * @link http://pear.php.net/package/PEAR
  49572. * @since Class available since Release 1.4.0a1
  49573. * @deprecated This class will disappear, and its components will be spread
  49574. * into smaller classes, like the AT&T breakup, as of Release 1.4.0a1
  49575. */
  49576. class PEAR_Common extends PEAR
  49577. {
  49578. /**
  49579. * User Interface object (PEAR_Frontend_* class). If null,
  49580. * the log() method uses print.
  49581. * @var object
  49582. */
  49583. var $ui = null;
  49584. /**
  49585. * Configuration object (PEAR_Config).
  49586. * @var PEAR_Config
  49587. */
  49588. var $config = null;
  49589. /** stack of elements, gives some sort of XML context */
  49590. var $element_stack = array();
  49591. /** name of currently parsed XML element */
  49592. var $current_element;
  49593. /** array of attributes of the currently parsed XML element */
  49594. var $current_attributes = array();
  49595. /** assoc with information about a package */
  49596. var $pkginfo = array();
  49597. var $current_path = null;
  49598. /**
  49599. * Flag variable used to mark a valid package file
  49600. * @var boolean
  49601. * @access private
  49602. */
  49603. var $_validPackageFile;
  49604. /**
  49605. * PEAR_Common constructor
  49606. *
  49607. * @access public
  49608. */
  49609. function __construct()
  49610. {
  49611. parent::__construct();
  49612. $this->config = &PEAR_Config::singleton();
  49613. $this->debug = $this->config->get('verbose');
  49614. }
  49615. /**
  49616. * PEAR_Common destructor
  49617. *
  49618. * @access private
  49619. */
  49620. function _PEAR_Common()
  49621. {
  49622. // doesn't work due to bug #14744
  49623. //$tempfiles = $this->_tempfiles;
  49624. $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles'];
  49625. while ($file = array_shift($tempfiles)) {
  49626. if (@is_dir($file)) {
  49627. if (!class_exists('System')) {
  49628. require_once 'System.php';
  49629. }
  49630. System::rm(array('-rf', $file));
  49631. } elseif (file_exists($file)) {
  49632. unlink($file);
  49633. }
  49634. }
  49635. }
  49636. /**
  49637. * Register a temporary file or directory. When the destructor is
  49638. * executed, all registered temporary files and directories are
  49639. * removed.
  49640. *
  49641. * @param string $file name of file or directory
  49642. *
  49643. * @return void
  49644. *
  49645. * @access public
  49646. */
  49647. static function addTempFile($file)
  49648. {
  49649. if (!class_exists('PEAR_Frontend')) {
  49650. require_once 'PEAR/Frontend.php';
  49651. }
  49652. PEAR_Frontend::addTempFile($file);
  49653. }
  49654. /**
  49655. * Wrapper to System::mkDir(), creates a directory as well as
  49656. * any necessary parent directories.
  49657. *
  49658. * @param string $dir directory name
  49659. *
  49660. * @return bool TRUE on success, or a PEAR error
  49661. *
  49662. * @access public
  49663. */
  49664. function mkDirHier($dir)
  49665. {
  49666. // Only used in Installer - move it there ?
  49667. $this->log(2, "+ create dir $dir");
  49668. if (!class_exists('System')) {
  49669. require_once 'System.php';
  49670. }
  49671. return System::mkDir(array('-p', $dir));
  49672. }
  49673. /**
  49674. * Logging method.
  49675. *
  49676. * @param int $level log level (0 is quiet, higher is noisier)
  49677. * @param string $msg message to write to the log
  49678. *
  49679. * @return void
  49680. */
  49681. public function log($level, $msg, $append_crlf = true)
  49682. {
  49683. if ($this->debug >= $level) {
  49684. if (!class_exists('PEAR_Frontend')) {
  49685. require_once 'PEAR/Frontend.php';
  49686. }
  49687. $ui = &PEAR_Frontend::singleton();
  49688. if (is_a($ui, 'PEAR_Frontend')) {
  49689. $ui->log($msg, $append_crlf);
  49690. } else {
  49691. print "$msg\n";
  49692. }
  49693. }
  49694. }
  49695. /**
  49696. * Create and register a temporary directory.
  49697. *
  49698. * @param string $tmpdir (optional) Directory to use as tmpdir.
  49699. * Will use system defaults (for example
  49700. * /tmp or c:\windows\temp) if not specified
  49701. *
  49702. * @return string name of created directory
  49703. *
  49704. * @access public
  49705. */
  49706. function mkTempDir($tmpdir = '')
  49707. {
  49708. $topt = $tmpdir ? array('-t', $tmpdir) : array();
  49709. $topt = array_merge($topt, array('-d', 'pear'));
  49710. if (!class_exists('System')) {
  49711. require_once 'System.php';
  49712. }
  49713. if (!$tmpdir = System::mktemp($topt)) {
  49714. return false;
  49715. }
  49716. self::addTempFile($tmpdir);
  49717. return $tmpdir;
  49718. }
  49719. /**
  49720. * Set object that represents the frontend to be used.
  49721. *
  49722. * @param object Reference of the frontend object
  49723. * @return void
  49724. * @access public
  49725. */
  49726. function setFrontendObject(&$ui)
  49727. {
  49728. $this->ui = &$ui;
  49729. }
  49730. /**
  49731. * Return an array containing all of the states that are more stable than
  49732. * or equal to the passed in state
  49733. *
  49734. * @param string Release state
  49735. * @param boolean Determines whether to include $state in the list
  49736. * @return false|array False if $state is not a valid release state
  49737. */
  49738. static function betterStates($state, $include = false)
  49739. {
  49740. static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
  49741. $i = array_search($state, $states);
  49742. if ($i === false) {
  49743. return false;
  49744. }
  49745. if ($include) {
  49746. $i--;
  49747. }
  49748. return array_slice($states, $i + 1);
  49749. }
  49750. /**
  49751. * Get the valid roles for a PEAR package maintainer
  49752. *
  49753. * @return array
  49754. */
  49755. public static function getUserRoles()
  49756. {
  49757. return $GLOBALS['_PEAR_Common_maintainer_roles'];
  49758. }
  49759. /**
  49760. * Get the valid package release states of packages
  49761. *
  49762. * @return array
  49763. */
  49764. public static function getReleaseStates()
  49765. {
  49766. return $GLOBALS['_PEAR_Common_release_states'];
  49767. }
  49768. /**
  49769. * Get the implemented dependency types (php, ext, pkg etc.)
  49770. *
  49771. * @return array
  49772. */
  49773. public static function getDependencyTypes()
  49774. {
  49775. return $GLOBALS['_PEAR_Common_dependency_types'];
  49776. }
  49777. /**
  49778. * Get the implemented dependency relations (has, lt, ge etc.)
  49779. *
  49780. * @return array
  49781. */
  49782. public static function getDependencyRelations()
  49783. {
  49784. return $GLOBALS['_PEAR_Common_dependency_relations'];
  49785. }
  49786. /**
  49787. * Get the implemented file roles
  49788. *
  49789. * @return array
  49790. */
  49791. public static function getFileRoles()
  49792. {
  49793. return $GLOBALS['_PEAR_Common_file_roles'];
  49794. }
  49795. /**
  49796. * Get the implemented file replacement types in
  49797. *
  49798. * @return array
  49799. */
  49800. public static function getReplacementTypes()
  49801. {
  49802. return $GLOBALS['_PEAR_Common_replacement_types'];
  49803. }
  49804. /**
  49805. * Get the implemented file replacement types in
  49806. *
  49807. * @return array
  49808. */
  49809. public static function getProvideTypes()
  49810. {
  49811. return $GLOBALS['_PEAR_Common_provide_types'];
  49812. }
  49813. /**
  49814. * Get the implemented file replacement types in
  49815. *
  49816. * @return array
  49817. */
  49818. public static function getScriptPhases()
  49819. {
  49820. return $GLOBALS['_PEAR_Common_script_phases'];
  49821. }
  49822. /**
  49823. * Test whether a string contains a valid package name.
  49824. *
  49825. * @param string $name the package name to test
  49826. *
  49827. * @return bool
  49828. *
  49829. * @access public
  49830. */
  49831. function validPackageName($name)
  49832. {
  49833. return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name);
  49834. }
  49835. /**
  49836. * Test whether a string contains a valid package version.
  49837. *
  49838. * @param string $ver the package version to test
  49839. *
  49840. * @return bool
  49841. *
  49842. * @access public
  49843. */
  49844. function validPackageVersion($ver)
  49845. {
  49846. return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
  49847. }
  49848. /**
  49849. * @param string $path relative or absolute include path
  49850. * @return boolean
  49851. */
  49852. public static function isIncludeable($path)
  49853. {
  49854. if (file_exists($path) && is_readable($path)) {
  49855. return true;
  49856. }
  49857. $ipath = explode(PATH_SEPARATOR, ini_get('include_path'));
  49858. foreach ($ipath as $include) {
  49859. $test = realpath($include . DIRECTORY_SEPARATOR . $path);
  49860. if (file_exists($test) && is_readable($test)) {
  49861. return true;
  49862. }
  49863. }
  49864. return false;
  49865. }
  49866. function _postProcessChecks($pf)
  49867. {
  49868. if (!PEAR::isError($pf)) {
  49869. return $this->_postProcessValidPackagexml($pf);
  49870. }
  49871. $errs = $pf->getUserinfo();
  49872. if (is_array($errs)) {
  49873. foreach ($errs as $error) {
  49874. $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
  49875. }
  49876. }
  49877. return $pf;
  49878. }
  49879. /**
  49880. * Returns information about a package file. Expects the name of
  49881. * a gzipped tar file as input.
  49882. *
  49883. * @param string $file name of .tgz file
  49884. *
  49885. * @return array array with package information
  49886. *
  49887. * @access public
  49888. * @deprecated use PEAR_PackageFile->fromTgzFile() instead
  49889. *
  49890. */
  49891. function infoFromTgzFile($file)
  49892. {
  49893. $packagefile = new PEAR_PackageFile($this->config);
  49894. $pf = &$packagefile->fromTgzFile($file, PEAR_VALIDATE_NORMAL);
  49895. return $this->_postProcessChecks($pf);
  49896. }
  49897. /**
  49898. * Returns information about a package file. Expects the name of
  49899. * a package xml file as input.
  49900. *
  49901. * @param string $descfile name of package xml file
  49902. *
  49903. * @return array array with package information
  49904. *
  49905. * @access public
  49906. * @deprecated use PEAR_PackageFile->fromPackageFile() instead
  49907. *
  49908. */
  49909. function infoFromDescriptionFile($descfile)
  49910. {
  49911. $packagefile = new PEAR_PackageFile($this->config);
  49912. $pf = &$packagefile->fromPackageFile($descfile, PEAR_VALIDATE_NORMAL);
  49913. return $this->_postProcessChecks($pf);
  49914. }
  49915. /**
  49916. * Returns information about a package file. Expects the contents
  49917. * of a package xml file as input.
  49918. *
  49919. * @param string $data contents of package.xml file
  49920. *
  49921. * @return array array with package information
  49922. *
  49923. * @access public
  49924. * @deprecated use PEAR_PackageFile->fromXmlstring() instead
  49925. *
  49926. */
  49927. function infoFromString($data)
  49928. {
  49929. $packagefile = new PEAR_PackageFile($this->config);
  49930. $pf = &$packagefile->fromXmlString($data, PEAR_VALIDATE_NORMAL, false);
  49931. return $this->_postProcessChecks($pf);
  49932. }
  49933. /**
  49934. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  49935. * @return array
  49936. */
  49937. function _postProcessValidPackagexml(&$pf)
  49938. {
  49939. if (!is_a($pf, 'PEAR_PackageFile_v2')) {
  49940. $this->pkginfo = $pf->toArray();
  49941. return $this->pkginfo;
  49942. }
  49943. // sort of make this into a package.xml 1.0-style array
  49944. // changelog is not converted to old format.
  49945. $arr = $pf->toArray(true);
  49946. $arr = array_merge($arr, $arr['old']);
  49947. unset($arr['old'], $arr['xsdversion'], $arr['contents'], $arr['compatible'],
  49948. $arr['channel'], $arr['uri'], $arr['dependencies'], $arr['phprelease'],
  49949. $arr['extsrcrelease'], $arr['zendextsrcrelease'], $arr['extbinrelease'],
  49950. $arr['zendextbinrelease'], $arr['bundle'], $arr['lead'], $arr['developer'],
  49951. $arr['helper'], $arr['contributor']);
  49952. $arr['filelist'] = $pf->getFilelist();
  49953. $this->pkginfo = $arr;
  49954. return $arr;
  49955. }
  49956. /**
  49957. * Returns package information from different sources
  49958. *
  49959. * This method is able to extract information about a package
  49960. * from a .tgz archive or from a XML package definition file.
  49961. *
  49962. * @access public
  49963. * @param string Filename of the source ('package.xml', '<package>.tgz')
  49964. * @return string
  49965. * @deprecated use PEAR_PackageFile->fromAnyFile() instead
  49966. */
  49967. function infoFromAny($info)
  49968. {
  49969. if (is_string($info) && file_exists($info)) {
  49970. $packagefile = new PEAR_PackageFile($this->config);
  49971. $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
  49972. if (PEAR::isError($pf)) {
  49973. $errs = $pf->getUserinfo();
  49974. if (is_array($errs)) {
  49975. foreach ($errs as $error) {
  49976. $e = $this->raiseError($error['message'], $error['code'], null, null, $error);
  49977. }
  49978. }
  49979. return $pf;
  49980. }
  49981. return $this->_postProcessValidPackagexml($pf);
  49982. }
  49983. return $info;
  49984. }
  49985. /**
  49986. * Return an XML document based on the package info (as returned
  49987. * by the PEAR_Common::infoFrom* methods).
  49988. *
  49989. * @param array $pkginfo package info
  49990. *
  49991. * @return string XML data
  49992. *
  49993. * @access public
  49994. * @deprecated use a PEAR_PackageFile_v* object's generator instead
  49995. */
  49996. function xmlFromInfo($pkginfo)
  49997. {
  49998. $config = &PEAR_Config::singleton();
  49999. $packagefile = new PEAR_PackageFile($config);
  50000. $pf = &$packagefile->fromArray($pkginfo);
  50001. $gen = &$pf->getDefaultGenerator();
  50002. return $gen->toXml(PEAR_VALIDATE_PACKAGING);
  50003. }
  50004. /**
  50005. * Validate XML package definition file.
  50006. *
  50007. * @param string $info Filename of the package archive or of the
  50008. * package definition file
  50009. * @param array $errors Array that will contain the errors
  50010. * @param array $warnings Array that will contain the warnings
  50011. * @param string $dir_prefix (optional) directory where source files
  50012. * may be found, or empty if they are not available
  50013. * @access public
  50014. * @return boolean
  50015. * @deprecated use the validation of PEAR_PackageFile objects
  50016. */
  50017. function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '')
  50018. {
  50019. $config = &PEAR_Config::singleton();
  50020. $packagefile = new PEAR_PackageFile($config);
  50021. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  50022. if (strpos($info, '<?xml') !== false) {
  50023. $pf = &$packagefile->fromXmlString($info, PEAR_VALIDATE_NORMAL, '');
  50024. } else {
  50025. $pf = &$packagefile->fromAnyFile($info, PEAR_VALIDATE_NORMAL);
  50026. }
  50027. PEAR::staticPopErrorHandling();
  50028. if (PEAR::isError($pf)) {
  50029. $errs = $pf->getUserinfo();
  50030. if (is_array($errs)) {
  50031. foreach ($errs as $error) {
  50032. if ($error['level'] == 'error') {
  50033. $errors[] = $error['message'];
  50034. } else {
  50035. $warnings[] = $error['message'];
  50036. }
  50037. }
  50038. }
  50039. return false;
  50040. }
  50041. return true;
  50042. }
  50043. /**
  50044. * Build a "provides" array from data returned by
  50045. * analyzeSourceCode(). The format of the built array is like
  50046. * this:
  50047. *
  50048. * array(
  50049. * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
  50050. * ...
  50051. * )
  50052. *
  50053. *
  50054. * @param array $srcinfo array with information about a source file
  50055. * as returned by the analyzeSourceCode() method.
  50056. *
  50057. * @return void
  50058. *
  50059. * @access public
  50060. *
  50061. */
  50062. function buildProvidesArray($srcinfo)
  50063. {
  50064. $file = basename($srcinfo['source_file']);
  50065. $pn = '';
  50066. if (isset($this->_packageName)) {
  50067. $pn = $this->_packageName;
  50068. }
  50069. $pnl = strlen($pn);
  50070. foreach ($srcinfo['declared_classes'] as $class) {
  50071. $key = "class;$class";
  50072. if (isset($this->pkginfo['provides'][$key])) {
  50073. continue;
  50074. }
  50075. $this->pkginfo['provides'][$key] =
  50076. array('file'=> $file, 'type' => 'class', 'name' => $class);
  50077. if (isset($srcinfo['inheritance'][$class])) {
  50078. $this->pkginfo['provides'][$key]['extends'] =
  50079. $srcinfo['inheritance'][$class];
  50080. }
  50081. }
  50082. foreach ($srcinfo['declared_methods'] as $class => $methods) {
  50083. foreach ($methods as $method) {
  50084. $function = "$class::$method";
  50085. $key = "function;$function";
  50086. if ($method[0] == '_' || !strcasecmp($method, $class) ||
  50087. isset($this->pkginfo['provides'][$key])) {
  50088. continue;
  50089. }
  50090. $this->pkginfo['provides'][$key] =
  50091. array('file'=> $file, 'type' => 'function', 'name' => $function);
  50092. }
  50093. }
  50094. foreach ($srcinfo['declared_functions'] as $function) {
  50095. $key = "function;$function";
  50096. if ($function[0] == '_' || isset($this->pkginfo['provides'][$key])) {
  50097. continue;
  50098. }
  50099. if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
  50100. $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
  50101. }
  50102. $this->pkginfo['provides'][$key] =
  50103. array('file'=> $file, 'type' => 'function', 'name' => $function);
  50104. }
  50105. }
  50106. /**
  50107. * Analyze the source code of the given PHP file
  50108. *
  50109. * @param string Filename of the PHP file
  50110. * @return mixed
  50111. * @access public
  50112. */
  50113. static function analyzeSourceCode($file)
  50114. {
  50115. if (!class_exists('PEAR_PackageFile_v2_Validator')) {
  50116. require_once 'PEAR/PackageFile/v2/Validator.php';
  50117. }
  50118. $a = new PEAR_PackageFile_v2_Validator;
  50119. return $a->analyzeSourceCode($file);
  50120. }
  50121. function detectDependencies($any, $status_callback = null)
  50122. {
  50123. if (!function_exists("token_get_all")) {
  50124. return false;
  50125. }
  50126. if (PEAR::isError($info = $this->infoFromAny($any))) {
  50127. return $this->raiseError($info);
  50128. }
  50129. if (!is_array($info)) {
  50130. return false;
  50131. }
  50132. $deps = array();
  50133. $used_c = $decl_c = $decl_f = $decl_m = array();
  50134. foreach ($info['filelist'] as $file => $fa) {
  50135. $tmp = $this->analyzeSourceCode($file);
  50136. $used_c = @array_merge($used_c, $tmp['used_classes']);
  50137. $decl_c = @array_merge($decl_c, $tmp['declared_classes']);
  50138. $decl_f = @array_merge($decl_f, $tmp['declared_functions']);
  50139. $decl_m = @array_merge($decl_m, $tmp['declared_methods']);
  50140. $inheri = @array_merge($inheri, $tmp['inheritance']);
  50141. }
  50142. $used_c = array_unique($used_c);
  50143. $decl_c = array_unique($decl_c);
  50144. $undecl_c = array_diff($used_c, $decl_c);
  50145. return array('used_classes' => $used_c,
  50146. 'declared_classes' => $decl_c,
  50147. 'declared_methods' => $decl_m,
  50148. 'declared_functions' => $decl_f,
  50149. 'undeclared_classes' => $undecl_c,
  50150. 'inheritance' => $inheri,
  50151. );
  50152. }
  50153. /**
  50154. * Download a file through HTTP. Considers suggested file name in
  50155. * Content-disposition: header and can run a callback function for
  50156. * different events. The callback will be called with two
  50157. * parameters: the callback type, and parameters. The implemented
  50158. * callback types are:
  50159. *
  50160. * 'setup' called at the very beginning, parameter is a UI object
  50161. * that should be used for all output
  50162. * 'message' the parameter is a string with an informational message
  50163. * 'saveas' may be used to save with a different file name, the
  50164. * parameter is the filename that is about to be used.
  50165. * If a 'saveas' callback returns a non-empty string,
  50166. * that file name will be used as the filename instead.
  50167. * Note that $save_dir will not be affected by this, only
  50168. * the basename of the file.
  50169. * 'start' download is starting, parameter is number of bytes
  50170. * that are expected, or -1 if unknown
  50171. * 'bytesread' parameter is the number of bytes read so far
  50172. * 'done' download is complete, parameter is the total number
  50173. * of bytes read
  50174. * 'connfailed' if the TCP connection fails, this callback is called
  50175. * with array(host,port,errno,errmsg)
  50176. * 'writefailed' if writing to disk fails, this callback is called
  50177. * with array(destfile,errmsg)
  50178. *
  50179. * If an HTTP proxy has been configured (http_proxy PEAR_Config
  50180. * setting), the proxy will be used.
  50181. *
  50182. * @param string $url the URL to download
  50183. * @param object $ui PEAR_Frontend_* instance
  50184. * @param object $config PEAR_Config instance
  50185. * @param string $save_dir (optional) directory to save file in
  50186. * @param mixed $callback (optional) function/method to call for status
  50187. * updates
  50188. * @param false|string|array $lastmodified header values to check against
  50189. * for caching
  50190. * use false to return the header
  50191. * values from this download
  50192. * @param false|array $accept Accept headers to send
  50193. * @param false|string $channel Channel to use for retrieving
  50194. * authentication
  50195. *
  50196. * @return mixed Returns the full path of the downloaded file or a PEAR
  50197. * error on failure. If the error is caused by
  50198. * socket-related errors, the error object will
  50199. * have the fsockopen error code available through
  50200. * getCode(). If caching is requested, then return the header
  50201. * values.
  50202. * If $lastmodified was given and the there are no changes,
  50203. * boolean false is returned.
  50204. *
  50205. * @access public
  50206. */
  50207. function downloadHttp(
  50208. $url, &$ui, $save_dir = '.', $callback = null, $lastmodified = null,
  50209. $accept = false, $channel = false
  50210. ) {
  50211. if (!class_exists('PEAR_Downloader')) {
  50212. require_once 'PEAR/Downloader.php';
  50213. }
  50214. return PEAR_Downloader::_downloadHttp(
  50215. $this, $url, $ui, $save_dir, $callback, $lastmodified,
  50216. $accept, $channel
  50217. );
  50218. }
  50219. }
  50220. require_once 'PEAR/Config.php';
  50221. require_once 'PEAR/PackageFile.php';
  50222. ��������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Config.php������������������������������������������������������������������������0000664�0001750�0001750�00000207035�14720722517�014744� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  50223. /**
  50224. * PEAR_Config, customized configuration handling for the PEAR Installer
  50225. *
  50226. * PHP versions 4 and 5
  50227. *
  50228. * @category pear
  50229. * @package PEAR
  50230. * @author Stig Bakken <ssb@php.net>
  50231. * @author Greg Beaver <cellog@php.net>
  50232. * @copyright 1997-2009 The Authors
  50233. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  50234. * @link http://pear.php.net/package/PEAR
  50235. * @since File available since Release 0.1
  50236. */
  50237. /**
  50238. * Required for error handling
  50239. */
  50240. require_once 'PEAR.php';
  50241. require_once 'PEAR/Registry.php';
  50242. require_once 'PEAR/Installer/Role.php';
  50243. require_once 'System.php';
  50244. /**
  50245. * Last created PEAR_Config instance.
  50246. * @var object
  50247. */
  50248. $GLOBALS['_PEAR_Config_instance'] = null;
  50249. if (!defined('PEAR_INSTALL_DIR') || !PEAR_INSTALL_DIR) {
  50250. $PEAR_INSTALL_DIR = PHP_LIBDIR . DIRECTORY_SEPARATOR . 'pear';
  50251. } else {
  50252. $PEAR_INSTALL_DIR = PEAR_INSTALL_DIR;
  50253. }
  50254. // Below we define constants with default values for all configuration
  50255. // parameters except username/password. All of them can have their
  50256. // defaults set through environment variables. The reason we use the
  50257. // PHP_ prefix is for some security, PHP protects environment
  50258. // variables starting with PHP_*.
  50259. // default channel and preferred mirror is based on whether we are invoked through
  50260. // the "pear" or the "pecl" command
  50261. if (!defined('PEAR_RUNTYPE')) {
  50262. define('PEAR_RUNTYPE', 'pear');
  50263. }
  50264. if (PEAR_RUNTYPE == 'pear') {
  50265. define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pear.php.net');
  50266. } else {
  50267. define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pecl.php.net');
  50268. }
  50269. if (getenv('PHP_PEAR_SYSCONF_DIR')) {
  50270. define('PEAR_CONFIG_SYSCONFDIR', getenv('PHP_PEAR_SYSCONF_DIR'));
  50271. } elseif (getenv('SystemRoot')) {
  50272. define('PEAR_CONFIG_SYSCONFDIR', getenv('SystemRoot'));
  50273. } else {
  50274. define('PEAR_CONFIG_SYSCONFDIR', PHP_SYSCONFDIR);
  50275. }
  50276. // Default for master_server
  50277. if (getenv('PHP_PEAR_MASTER_SERVER')) {
  50278. define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', getenv('PHP_PEAR_MASTER_SERVER'));
  50279. } else {
  50280. define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', 'pear.php.net');
  50281. }
  50282. // Default for http_proxy
  50283. if (getenv('PHP_PEAR_HTTP_PROXY')) {
  50284. define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('PHP_PEAR_HTTP_PROXY'));
  50285. } elseif (getenv('http_proxy')) {
  50286. define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('http_proxy'));
  50287. } else {
  50288. define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', '');
  50289. }
  50290. // Default for php_dir
  50291. if (getenv('PHP_PEAR_INSTALL_DIR')) {
  50292. define('PEAR_CONFIG_DEFAULT_PHP_DIR', getenv('PHP_PEAR_INSTALL_DIR'));
  50293. } else {
  50294. define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR);
  50295. }
  50296. // Default for metadata_dir
  50297. if (getenv('PHP_PEAR_METADATA_DIR')) {
  50298. define('PEAR_CONFIG_DEFAULT_METADATA_DIR', getenv('PHP_PEAR_METADATA_DIR'));
  50299. } else {
  50300. define('PEAR_CONFIG_DEFAULT_METADATA_DIR', '');
  50301. }
  50302. // Default for ext_dir
  50303. if (getenv('PHP_PEAR_EXTENSION_DIR')) {
  50304. define('PEAR_CONFIG_DEFAULT_EXT_DIR', getenv('PHP_PEAR_EXTENSION_DIR'));
  50305. } else {
  50306. if (ini_get('extension_dir')) {
  50307. define('PEAR_CONFIG_DEFAULT_EXT_DIR', ini_get('extension_dir'));
  50308. } elseif (defined('PEAR_EXTENSION_DIR') &&
  50309. file_exists(PEAR_EXTENSION_DIR) && is_dir(PEAR_EXTENSION_DIR)) {
  50310. define('PEAR_CONFIG_DEFAULT_EXT_DIR', PEAR_EXTENSION_DIR);
  50311. } elseif (defined('PHP_EXTENSION_DIR')) {
  50312. define('PEAR_CONFIG_DEFAULT_EXT_DIR', PHP_EXTENSION_DIR);
  50313. } else {
  50314. define('PEAR_CONFIG_DEFAULT_EXT_DIR', '.');
  50315. }
  50316. }
  50317. // Default for doc_dir
  50318. if (getenv('PHP_PEAR_DOC_DIR')) {
  50319. define('PEAR_CONFIG_DEFAULT_DOC_DIR', getenv('PHP_PEAR_DOC_DIR'));
  50320. } else {
  50321. define('PEAR_CONFIG_DEFAULT_DOC_DIR',
  50322. $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'docs');
  50323. }
  50324. // Default for bin_dir
  50325. if (getenv('PHP_PEAR_BIN_DIR')) {
  50326. define('PEAR_CONFIG_DEFAULT_BIN_DIR', getenv('PHP_PEAR_BIN_DIR'));
  50327. } else {
  50328. define('PEAR_CONFIG_DEFAULT_BIN_DIR', PHP_BINDIR);
  50329. }
  50330. // Default for data_dir
  50331. if (getenv('PHP_PEAR_DATA_DIR')) {
  50332. define('PEAR_CONFIG_DEFAULT_DATA_DIR', getenv('PHP_PEAR_DATA_DIR'));
  50333. } else {
  50334. define('PEAR_CONFIG_DEFAULT_DATA_DIR',
  50335. $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'data');
  50336. }
  50337. // Default for cfg_dir
  50338. if (getenv('PHP_PEAR_CFG_DIR')) {
  50339. define('PEAR_CONFIG_DEFAULT_CFG_DIR', getenv('PHP_PEAR_CFG_DIR'));
  50340. } else {
  50341. define('PEAR_CONFIG_DEFAULT_CFG_DIR',
  50342. $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'cfg');
  50343. }
  50344. // Default for www_dir
  50345. if (getenv('PHP_PEAR_WWW_DIR')) {
  50346. define('PEAR_CONFIG_DEFAULT_WWW_DIR', getenv('PHP_PEAR_WWW_DIR'));
  50347. } else {
  50348. define('PEAR_CONFIG_DEFAULT_WWW_DIR',
  50349. $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'www');
  50350. }
  50351. // Default for man_dir
  50352. if (getenv('PHP_PEAR_MAN_DIR')) {
  50353. define('PEAR_CONFIG_DEFAULT_MAN_DIR', getenv('PHP_PEAR_MAN_DIR'));
  50354. } else {
  50355. if (defined('PHP_MANDIR')) { // Added in PHP5.3.7
  50356. define('PEAR_CONFIG_DEFAULT_MAN_DIR', PHP_MANDIR);
  50357. } else {
  50358. define('PEAR_CONFIG_DEFAULT_MAN_DIR', PHP_PREFIX . DIRECTORY_SEPARATOR .
  50359. 'local' . DIRECTORY_SEPARATOR .'man');
  50360. }
  50361. }
  50362. // Default for test_dir
  50363. if (getenv('PHP_PEAR_TEST_DIR')) {
  50364. define('PEAR_CONFIG_DEFAULT_TEST_DIR', getenv('PHP_PEAR_TEST_DIR'));
  50365. } else {
  50366. define('PEAR_CONFIG_DEFAULT_TEST_DIR',
  50367. $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'tests');
  50368. }
  50369. // Default for temp_dir
  50370. if (getenv('PHP_PEAR_TEMP_DIR')) {
  50371. define('PEAR_CONFIG_DEFAULT_TEMP_DIR', getenv('PHP_PEAR_TEMP_DIR'));
  50372. } else {
  50373. define('PEAR_CONFIG_DEFAULT_TEMP_DIR',
  50374. System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
  50375. DIRECTORY_SEPARATOR . 'temp');
  50376. }
  50377. // Default for cache_dir
  50378. if (getenv('PHP_PEAR_CACHE_DIR')) {
  50379. define('PEAR_CONFIG_DEFAULT_CACHE_DIR', getenv('PHP_PEAR_CACHE_DIR'));
  50380. } else {
  50381. define('PEAR_CONFIG_DEFAULT_CACHE_DIR',
  50382. System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
  50383. DIRECTORY_SEPARATOR . 'cache');
  50384. }
  50385. // Default for download_dir
  50386. if (getenv('PHP_PEAR_DOWNLOAD_DIR')) {
  50387. define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR', getenv('PHP_PEAR_DOWNLOAD_DIR'));
  50388. } else {
  50389. define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR',
  50390. System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
  50391. DIRECTORY_SEPARATOR . 'download');
  50392. }
  50393. // Default for php_bin
  50394. if (getenv('PHP_PEAR_PHP_BIN')) {
  50395. define('PEAR_CONFIG_DEFAULT_PHP_BIN', getenv('PHP_PEAR_PHP_BIN'));
  50396. } else {
  50397. define('PEAR_CONFIG_DEFAULT_PHP_BIN', PEAR_CONFIG_DEFAULT_BIN_DIR.
  50398. DIRECTORY_SEPARATOR.'php'.(OS_WINDOWS ? '.exe' : ''));
  50399. }
  50400. // Default for verbose
  50401. if (getenv('PHP_PEAR_VERBOSE')) {
  50402. define('PEAR_CONFIG_DEFAULT_VERBOSE', getenv('PHP_PEAR_VERBOSE'));
  50403. } else {
  50404. define('PEAR_CONFIG_DEFAULT_VERBOSE', 1);
  50405. }
  50406. // Default for preferred_state
  50407. if (getenv('PHP_PEAR_PREFERRED_STATE')) {
  50408. define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', getenv('PHP_PEAR_PREFERRED_STATE'));
  50409. } else {
  50410. define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', 'stable');
  50411. }
  50412. // Default for umask
  50413. if (getenv('PHP_PEAR_UMASK')) {
  50414. define('PEAR_CONFIG_DEFAULT_UMASK', getenv('PHP_PEAR_UMASK'));
  50415. } else {
  50416. define('PEAR_CONFIG_DEFAULT_UMASK', decoct(umask()));
  50417. }
  50418. // Default for cache_ttl
  50419. if (getenv('PHP_PEAR_CACHE_TTL')) {
  50420. define('PEAR_CONFIG_DEFAULT_CACHE_TTL', getenv('PHP_PEAR_CACHE_TTL'));
  50421. } else {
  50422. define('PEAR_CONFIG_DEFAULT_CACHE_TTL', 3600);
  50423. }
  50424. // Default for sig_type
  50425. if (getenv('PHP_PEAR_SIG_TYPE')) {
  50426. define('PEAR_CONFIG_DEFAULT_SIG_TYPE', getenv('PHP_PEAR_SIG_TYPE'));
  50427. } else {
  50428. define('PEAR_CONFIG_DEFAULT_SIG_TYPE', 'gpg');
  50429. }
  50430. // Default for sig_bin
  50431. if (getenv('PHP_PEAR_SIG_BIN')) {
  50432. define('PEAR_CONFIG_DEFAULT_SIG_BIN', getenv('PHP_PEAR_SIG_BIN'));
  50433. } else {
  50434. define('PEAR_CONFIG_DEFAULT_SIG_BIN',
  50435. System::which(
  50436. 'gpg', OS_WINDOWS ? 'c:\gnupg\gpg.exe' : '/usr/local/bin/gpg'));
  50437. }
  50438. // Default for sig_keydir
  50439. if (getenv('PHP_PEAR_SIG_KEYDIR')) {
  50440. define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', getenv('PHP_PEAR_SIG_KEYDIR'));
  50441. } else {
  50442. define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR',
  50443. PEAR_CONFIG_SYSCONFDIR . DIRECTORY_SEPARATOR . 'pearkeys');
  50444. }
  50445. /**
  50446. * This is a class for storing configuration data, keeping track of
  50447. * which are system-defined, user-defined or defaulted.
  50448. * @category pear
  50449. * @package PEAR
  50450. * @author Stig Bakken <ssb@php.net>
  50451. * @author Greg Beaver <cellog@php.net>
  50452. * @copyright 1997-2009 The Authors
  50453. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  50454. * @version Release: 1.10.16
  50455. * @link http://pear.php.net/package/PEAR
  50456. * @since Class available since Release 0.1
  50457. */
  50458. class PEAR_Config extends PEAR
  50459. {
  50460. /**
  50461. * Array of config files used.
  50462. *
  50463. * @var array layer => config file
  50464. */
  50465. var $files = array(
  50466. 'system' => '',
  50467. 'user' => '',
  50468. );
  50469. var $layers = array();
  50470. /**
  50471. * Configuration data, two-dimensional array where the first
  50472. * dimension is the config layer ('user', 'system' and 'default'),
  50473. * and the second dimension is keyname => value.
  50474. *
  50475. * The order in the first dimension is important! Earlier
  50476. * layers will shadow later ones when a config value is
  50477. * requested (if a 'user' value exists, it will be returned first,
  50478. * then 'system' and finally 'default').
  50479. *
  50480. * @var array layer => array(keyname => value, ...)
  50481. */
  50482. var $configuration = array(
  50483. 'user' => array(),
  50484. 'system' => array(),
  50485. 'default' => array(),
  50486. );
  50487. /**
  50488. * Configuration values that can be set for a channel
  50489. *
  50490. * All other configuration values can only have a global value
  50491. * @var array
  50492. * @access private
  50493. */
  50494. var $_channelConfigInfo = array(
  50495. 'php_dir', 'ext_dir', 'doc_dir', 'bin_dir', 'data_dir', 'cfg_dir',
  50496. 'test_dir', 'www_dir', 'php_bin', 'php_prefix', 'php_suffix', 'username',
  50497. 'password', 'verbose', 'preferred_state', 'umask', 'preferred_mirror', 'php_ini'
  50498. );
  50499. /**
  50500. * Channels that can be accessed
  50501. * @see setChannels()
  50502. * @var array
  50503. * @access private
  50504. */
  50505. var $_channels = array('pear.php.net', 'pecl.php.net', '__uri');
  50506. /**
  50507. * This variable is used to control the directory values returned
  50508. * @see setInstallRoot();
  50509. * @var string|false
  50510. * @access private
  50511. */
  50512. var $_installRoot = false;
  50513. /**
  50514. * If requested, this will always refer to the registry
  50515. * contained in php_dir
  50516. * @var PEAR_Registry
  50517. */
  50518. var $_registry = array();
  50519. /**
  50520. * @var array
  50521. * @access private
  50522. */
  50523. var $_regInitialized = array();
  50524. /**
  50525. * @var bool
  50526. * @access private
  50527. */
  50528. var $_noRegistry = false;
  50529. /**
  50530. * amount of errors found while parsing config
  50531. * @var integer
  50532. * @access private
  50533. */
  50534. var $_errorsFound = 0;
  50535. var $_lastError = null;
  50536. /**
  50537. * Information about the configuration data. Stores the type,
  50538. * default value and a documentation string for each configuration
  50539. * value.
  50540. *
  50541. * @var array layer => array(infotype => value, ...)
  50542. */
  50543. var $configuration_info = array(
  50544. // Channels/Internet Access
  50545. 'default_channel' => array(
  50546. 'type' => 'string',
  50547. 'default' => PEAR_CONFIG_DEFAULT_CHANNEL,
  50548. 'doc' => 'the default channel to use for all non explicit commands',
  50549. 'prompt' => 'Default Channel',
  50550. 'group' => 'Internet Access',
  50551. ),
  50552. 'preferred_mirror' => array(
  50553. 'type' => 'string',
  50554. 'default' => PEAR_CONFIG_DEFAULT_CHANNEL,
  50555. 'doc' => 'the default server or mirror to use for channel actions',
  50556. 'prompt' => 'Default Channel Mirror',
  50557. 'group' => 'Internet Access',
  50558. ),
  50559. 'remote_config' => array(
  50560. 'type' => 'password',
  50561. 'default' => '',
  50562. 'doc' => 'ftp url of remote configuration file to use for synchronized install',
  50563. 'prompt' => 'Remote Configuration File',
  50564. 'group' => 'Internet Access',
  50565. ),
  50566. 'auto_discover' => array(
  50567. 'type' => 'integer',
  50568. 'default' => 0,
  50569. 'doc' => 'whether to automatically discover new channels',
  50570. 'prompt' => 'Auto-discover new Channels',
  50571. 'group' => 'Internet Access',
  50572. ),
  50573. // Internet Access
  50574. 'master_server' => array(
  50575. 'type' => 'string',
  50576. 'default' => 'pear.php.net',
  50577. 'doc' => 'name of the main PEAR server [NOT USED IN THIS VERSION]',
  50578. 'prompt' => 'PEAR server [DEPRECATED]',
  50579. 'group' => 'Internet Access',
  50580. ),
  50581. 'http_proxy' => array(
  50582. 'type' => 'string',
  50583. 'default' => PEAR_CONFIG_DEFAULT_HTTP_PROXY,
  50584. 'doc' => 'HTTP proxy (host:port) to use when downloading packages',
  50585. 'prompt' => 'HTTP Proxy Server Address',
  50586. 'group' => 'Internet Access',
  50587. ),
  50588. // File Locations
  50589. 'php_dir' => array(
  50590. 'type' => 'directory',
  50591. 'default' => PEAR_CONFIG_DEFAULT_PHP_DIR,
  50592. 'doc' => 'directory where .php files are installed',
  50593. 'prompt' => 'PEAR directory',
  50594. 'group' => 'File Locations',
  50595. ),
  50596. 'ext_dir' => array(
  50597. 'type' => 'directory',
  50598. 'default' => PEAR_CONFIG_DEFAULT_EXT_DIR,
  50599. 'doc' => 'directory where loadable extensions are installed',
  50600. 'prompt' => 'PHP extension directory',
  50601. 'group' => 'File Locations',
  50602. ),
  50603. 'doc_dir' => array(
  50604. 'type' => 'directory',
  50605. 'default' => PEAR_CONFIG_DEFAULT_DOC_DIR,
  50606. 'doc' => 'directory where documentation is installed',
  50607. 'prompt' => 'PEAR documentation directory',
  50608. 'group' => 'File Locations',
  50609. ),
  50610. 'bin_dir' => array(
  50611. 'type' => 'directory',
  50612. 'default' => PEAR_CONFIG_DEFAULT_BIN_DIR,
  50613. 'doc' => 'directory where executables are installed',
  50614. 'prompt' => 'PEAR executables directory',
  50615. 'group' => 'File Locations',
  50616. ),
  50617. 'data_dir' => array(
  50618. 'type' => 'directory',
  50619. 'default' => PEAR_CONFIG_DEFAULT_DATA_DIR,
  50620. 'doc' => 'directory where data files are installed',
  50621. 'prompt' => 'PEAR data directory',
  50622. 'group' => 'File Locations (Advanced)',
  50623. ),
  50624. 'cfg_dir' => array(
  50625. 'type' => 'directory',
  50626. 'default' => PEAR_CONFIG_DEFAULT_CFG_DIR,
  50627. 'doc' => 'directory where modifiable configuration files are installed',
  50628. 'prompt' => 'PEAR configuration file directory',
  50629. 'group' => 'File Locations (Advanced)',
  50630. ),
  50631. 'www_dir' => array(
  50632. 'type' => 'directory',
  50633. 'default' => PEAR_CONFIG_DEFAULT_WWW_DIR,
  50634. 'doc' => 'directory where www frontend files (html/js) are installed',
  50635. 'prompt' => 'PEAR www files directory',
  50636. 'group' => 'File Locations (Advanced)',
  50637. ),
  50638. 'man_dir' => array(
  50639. 'type' => 'directory',
  50640. 'default' => PEAR_CONFIG_DEFAULT_MAN_DIR,
  50641. 'doc' => 'directory where unix manual pages are installed',
  50642. 'prompt' => 'Systems manpage files directory',
  50643. 'group' => 'File Locations (Advanced)',
  50644. ),
  50645. 'test_dir' => array(
  50646. 'type' => 'directory',
  50647. 'default' => PEAR_CONFIG_DEFAULT_TEST_DIR,
  50648. 'doc' => 'directory where regression tests are installed',
  50649. 'prompt' => 'PEAR test directory',
  50650. 'group' => 'File Locations (Advanced)',
  50651. ),
  50652. 'cache_dir' => array(
  50653. 'type' => 'directory',
  50654. 'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR,
  50655. 'doc' => 'directory which is used for web service cache',
  50656. 'prompt' => 'PEAR Installer cache directory',
  50657. 'group' => 'File Locations (Advanced)',
  50658. ),
  50659. 'temp_dir' => array(
  50660. 'type' => 'directory',
  50661. 'default' => PEAR_CONFIG_DEFAULT_TEMP_DIR,
  50662. 'doc' => 'directory which is used for all temp files',
  50663. 'prompt' => 'PEAR Installer temp directory',
  50664. 'group' => 'File Locations (Advanced)',
  50665. ),
  50666. 'download_dir' => array(
  50667. 'type' => 'directory',
  50668. 'default' => PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR,
  50669. 'doc' => 'directory which is used for all downloaded files',
  50670. 'prompt' => 'PEAR Installer download directory',
  50671. 'group' => 'File Locations (Advanced)',
  50672. ),
  50673. 'php_bin' => array(
  50674. 'type' => 'file',
  50675. 'default' => PEAR_CONFIG_DEFAULT_PHP_BIN,
  50676. 'doc' => 'PHP CLI/CGI binary for executing scripts',
  50677. 'prompt' => 'PHP CLI/CGI binary',
  50678. 'group' => 'File Locations (Advanced)',
  50679. ),
  50680. 'php_prefix' => array(
  50681. 'type' => 'string',
  50682. 'default' => '',
  50683. 'doc' => '--program-prefix for php_bin\'s ./configure, used for pecl installs',
  50684. 'prompt' => '--program-prefix passed to PHP\'s ./configure',
  50685. 'group' => 'File Locations (Advanced)',
  50686. ),
  50687. 'php_suffix' => array(
  50688. 'type' => 'string',
  50689. 'default' => '',
  50690. 'doc' => '--program-suffix for php_bin\'s ./configure, used for pecl installs',
  50691. 'prompt' => '--program-suffix passed to PHP\'s ./configure',
  50692. 'group' => 'File Locations (Advanced)',
  50693. ),
  50694. 'php_ini' => array(
  50695. 'type' => 'file',
  50696. 'default' => '',
  50697. 'doc' => 'location of php.ini in which to enable PECL extensions on install',
  50698. 'prompt' => 'php.ini location',
  50699. 'group' => 'File Locations (Advanced)',
  50700. ),
  50701. 'metadata_dir' => array(
  50702. 'type' => 'directory',
  50703. 'default' => PEAR_CONFIG_DEFAULT_METADATA_DIR,
  50704. 'doc' => 'directory where metadata files are installed (registry, filemap, channels, ...)',
  50705. 'prompt' => 'PEAR metadata directory',
  50706. 'group' => 'File Locations (Advanced)',
  50707. ),
  50708. // Maintainers
  50709. 'username' => array(
  50710. 'type' => 'string',
  50711. 'default' => '',
  50712. 'doc' => '(maintainers) your PEAR account name',
  50713. 'prompt' => 'PEAR username (for maintainers)',
  50714. 'group' => 'Maintainers',
  50715. ),
  50716. 'password' => array(
  50717. 'type' => 'password',
  50718. 'default' => '',
  50719. 'doc' => '(maintainers) your PEAR account password',
  50720. 'prompt' => 'PEAR password (for maintainers)',
  50721. 'group' => 'Maintainers',
  50722. ),
  50723. // Advanced
  50724. 'verbose' => array(
  50725. 'type' => 'integer',
  50726. 'default' => PEAR_CONFIG_DEFAULT_VERBOSE,
  50727. 'doc' => 'verbosity level
  50728. 0: really quiet
  50729. 1: somewhat quiet
  50730. 2: verbose
  50731. 3: debug',
  50732. 'prompt' => 'Debug Log Level',
  50733. 'group' => 'Advanced',
  50734. ),
  50735. 'preferred_state' => array(
  50736. 'type' => 'set',
  50737. 'default' => PEAR_CONFIG_DEFAULT_PREFERRED_STATE,
  50738. 'doc' => 'the installer will prefer releases with this state when installing packages without a version or state specified',
  50739. 'valid_set' => array(
  50740. 'stable', 'beta', 'alpha', 'devel', 'snapshot'),
  50741. 'prompt' => 'Preferred Package State',
  50742. 'group' => 'Advanced',
  50743. ),
  50744. 'umask' => array(
  50745. 'type' => 'mask',
  50746. 'default' => PEAR_CONFIG_DEFAULT_UMASK,
  50747. 'doc' => 'umask used when creating files (Unix-like systems only)',
  50748. 'prompt' => 'Unix file mask',
  50749. 'group' => 'Advanced',
  50750. ),
  50751. 'cache_ttl' => array(
  50752. 'type' => 'integer',
  50753. 'default' => PEAR_CONFIG_DEFAULT_CACHE_TTL,
  50754. 'doc' => 'amount of secs where the local cache is used and not updated',
  50755. 'prompt' => 'Cache TimeToLive',
  50756. 'group' => 'Advanced',
  50757. ),
  50758. 'sig_type' => array(
  50759. 'type' => 'set',
  50760. 'default' => PEAR_CONFIG_DEFAULT_SIG_TYPE,
  50761. 'doc' => 'which package signature mechanism to use',
  50762. 'valid_set' => array('gpg'),
  50763. 'prompt' => 'Package Signature Type',
  50764. 'group' => 'Maintainers',
  50765. ),
  50766. 'sig_bin' => array(
  50767. 'type' => 'string',
  50768. 'default' => PEAR_CONFIG_DEFAULT_SIG_BIN,
  50769. 'doc' => 'which package signature mechanism to use',
  50770. 'prompt' => 'Signature Handling Program',
  50771. 'group' => 'Maintainers',
  50772. ),
  50773. 'sig_keyid' => array(
  50774. 'type' => 'string',
  50775. 'default' => '',
  50776. 'doc' => 'which key to use for signing with',
  50777. 'prompt' => 'Signature Key Id',
  50778. 'group' => 'Maintainers',
  50779. ),
  50780. 'sig_keydir' => array(
  50781. 'type' => 'directory',
  50782. 'default' => PEAR_CONFIG_DEFAULT_SIG_KEYDIR,
  50783. 'doc' => 'directory where signature keys are located',
  50784. 'prompt' => 'Signature Key Directory',
  50785. 'group' => 'Maintainers',
  50786. ),
  50787. // __channels is reserved - used for channel-specific configuration
  50788. );
  50789. /**
  50790. * Constructor.
  50791. *
  50792. * @param string file to read user-defined options from
  50793. * @param string file to read system-wide defaults from
  50794. * @param bool determines whether a registry object "follows"
  50795. * the value of php_dir (is automatically created
  50796. * and moved when php_dir is changed)
  50797. * @param bool if true, fails if configuration files cannot be loaded
  50798. *
  50799. * @access public
  50800. *
  50801. * @see PEAR_Config::singleton
  50802. */
  50803. function __construct($user_file = '', $system_file = '', $ftp_file = false,
  50804. $strict = true)
  50805. {
  50806. parent::__construct();
  50807. PEAR_Installer_Role::initializeConfig($this);
  50808. $sl = DIRECTORY_SEPARATOR;
  50809. if (empty($user_file)) {
  50810. if (OS_WINDOWS) {
  50811. $user_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini';
  50812. } else {
  50813. $user_file = getenv('HOME') . $sl . '.pearrc';
  50814. }
  50815. }
  50816. if (empty($system_file)) {
  50817. $system_file = PEAR_CONFIG_SYSCONFDIR . $sl;
  50818. if (OS_WINDOWS) {
  50819. $system_file .= 'pearsys.ini';
  50820. } else {
  50821. $system_file .= 'pear.conf';
  50822. }
  50823. }
  50824. $this->layers = array_keys($this->configuration);
  50825. $this->files['user'] = $user_file;
  50826. $this->files['system'] = $system_file;
  50827. if ($user_file && file_exists($user_file)) {
  50828. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  50829. $this->readConfigFile($user_file, 'user', $strict);
  50830. $this->popErrorHandling();
  50831. if ($this->_errorsFound > 0) {
  50832. return;
  50833. }
  50834. }
  50835. if ($system_file && @file_exists($system_file)) {
  50836. $this->mergeConfigFile($system_file, false, 'system', $strict);
  50837. if ($this->_errorsFound > 0) {
  50838. return;
  50839. }
  50840. }
  50841. if (!$ftp_file) {
  50842. $ftp_file = $this->get('remote_config');
  50843. }
  50844. if ($ftp_file && defined('PEAR_REMOTEINSTALL_OK')) {
  50845. $this->readFTPConfigFile($ftp_file);
  50846. }
  50847. foreach ($this->configuration_info as $key => $info) {
  50848. $this->configuration['default'][$key] = $info['default'];
  50849. }
  50850. $this->_registry['default'] = new PEAR_Registry(
  50851. $this->configuration['default']['php_dir'], false, false,
  50852. $this->configuration['default']['metadata_dir']);
  50853. $this->_registry['default']->setConfig($this, false);
  50854. $this->_regInitialized['default'] = false;
  50855. //$GLOBALS['_PEAR_Config_instance'] = &$this;
  50856. }
  50857. /**
  50858. * Return the default locations of user and system configuration files
  50859. */
  50860. public static function getDefaultConfigFiles()
  50861. {
  50862. $sl = DIRECTORY_SEPARATOR;
  50863. if (OS_WINDOWS) {
  50864. return array(
  50865. 'user' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini',
  50866. 'system' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini'
  50867. );
  50868. }
  50869. return array(
  50870. 'user' => getenv('HOME') . $sl . '.pearrc',
  50871. 'system' => PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf'
  50872. );
  50873. }
  50874. /**
  50875. * Static singleton method. If you want to keep only one instance
  50876. * of this class in use, this method will give you a reference to
  50877. * the last created PEAR_Config object if one exists, or create a
  50878. * new object.
  50879. *
  50880. * @param string (optional) file to read user-defined options from
  50881. * @param string (optional) file to read system-wide defaults from
  50882. *
  50883. * @return object an existing or new PEAR_Config instance
  50884. *
  50885. * @see PEAR_Config::PEAR_Config
  50886. */
  50887. public static function &singleton($user_file = '', $system_file = '', $strict = true)
  50888. {
  50889. if (is_object($GLOBALS['_PEAR_Config_instance'])) {
  50890. return $GLOBALS['_PEAR_Config_instance'];
  50891. }
  50892. $t_conf = new PEAR_Config($user_file, $system_file, false, $strict);
  50893. if ($t_conf->_errorsFound > 0) {
  50894. return $t_conf->_lastError;
  50895. }
  50896. $GLOBALS['_PEAR_Config_instance'] = &$t_conf;
  50897. return $GLOBALS['_PEAR_Config_instance'];
  50898. }
  50899. /**
  50900. * Determine whether any configuration files have been detected, and whether a
  50901. * registry object can be retrieved from this configuration.
  50902. * @return bool
  50903. * @since PEAR 1.4.0a1
  50904. */
  50905. function validConfiguration()
  50906. {
  50907. if ($this->isDefinedLayer('user') || $this->isDefinedLayer('system')) {
  50908. return true;
  50909. }
  50910. return false;
  50911. }
  50912. /**
  50913. * Reads configuration data from a file. All existing values in
  50914. * the config layer are discarded and replaced with data from the
  50915. * file.
  50916. * @param string file to read from, if NULL or not specified, the
  50917. * last-used file for the same layer (second param) is used
  50918. * @param string config layer to insert data into ('user' or 'system')
  50919. * @return bool TRUE on success or a PEAR error on failure
  50920. */
  50921. function readConfigFile($file = null, $layer = 'user', $strict = true)
  50922. {
  50923. if (empty($this->files[$layer])) {
  50924. return $this->raiseError("unknown config layer `$layer'");
  50925. }
  50926. if ($file === null) {
  50927. $file = $this->files[$layer];
  50928. }
  50929. $data = $this->_readConfigDataFrom($file);
  50930. if (PEAR::isError($data)) {
  50931. if (!$strict) {
  50932. return true;
  50933. }
  50934. $this->_errorsFound++;
  50935. $this->_lastError = $data;
  50936. return $data;
  50937. }
  50938. $this->files[$layer] = $file;
  50939. $this->_decodeInput($data);
  50940. $this->configuration[$layer] = $data;
  50941. $this->_setupChannels();
  50942. if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) {
  50943. $this->_registry[$layer] = new PEAR_Registry(
  50944. $phpdir, false, false,
  50945. $this->get('metadata_dir', $layer, 'pear.php.net'));
  50946. $this->_registry[$layer]->setConfig($this, false);
  50947. $this->_regInitialized[$layer] = false;
  50948. } else {
  50949. unset($this->_registry[$layer]);
  50950. }
  50951. return true;
  50952. }
  50953. /**
  50954. * @param string url to the remote config file, like ftp://www.example.com/pear/config.ini
  50955. * @return true|PEAR_Error
  50956. */
  50957. function readFTPConfigFile($path)
  50958. {
  50959. do { // poor man's try
  50960. if (!class_exists('PEAR_FTP')) {
  50961. if (!class_exists('PEAR_Common')) {
  50962. require_once 'PEAR/Common.php';
  50963. }
  50964. if (PEAR_Common::isIncludeable('PEAR/FTP.php')) {
  50965. require_once 'PEAR/FTP.php';
  50966. }
  50967. }
  50968. if (!class_exists('PEAR_FTP')) {
  50969. return PEAR::raiseError('PEAR_RemoteInstaller must be installed to use remote config');
  50970. }
  50971. $this->_ftp = new PEAR_FTP;
  50972. $this->_ftp->pushErrorHandling(PEAR_ERROR_RETURN);
  50973. $e = $this->_ftp->init($path);
  50974. if (PEAR::isError($e)) {
  50975. $this->_ftp->popErrorHandling();
  50976. return $e;
  50977. }
  50978. $tmp = System::mktemp('-d');
  50979. PEAR_Common::addTempFile($tmp);
  50980. $e = $this->_ftp->get(basename($path), $tmp . DIRECTORY_SEPARATOR .
  50981. 'pear.ini', false, FTP_BINARY);
  50982. if (PEAR::isError($e)) {
  50983. $this->_ftp->popErrorHandling();
  50984. return $e;
  50985. }
  50986. PEAR_Common::addTempFile($tmp . DIRECTORY_SEPARATOR . 'pear.ini');
  50987. $this->_ftp->disconnect();
  50988. $this->_ftp->popErrorHandling();
  50989. $this->files['ftp'] = $tmp . DIRECTORY_SEPARATOR . 'pear.ini';
  50990. $e = $this->readConfigFile(null, 'ftp');
  50991. if (PEAR::isError($e)) {
  50992. return $e;
  50993. }
  50994. $fail = array();
  50995. foreach ($this->configuration_info as $key => $val) {
  50996. if (in_array($this->getGroup($key),
  50997. array('File Locations', 'File Locations (Advanced)')) &&
  50998. $this->getType($key) == 'directory') {
  50999. // any directory configs must be set for this to work
  51000. if (!isset($this->configuration['ftp'][$key])) {
  51001. $fail[] = $key;
  51002. }
  51003. }
  51004. }
  51005. if (!count($fail)) {
  51006. return true;
  51007. }
  51008. $fail = '"' . implode('", "', $fail) . '"';
  51009. unset($this->files['ftp']);
  51010. unset($this->configuration['ftp']);
  51011. return PEAR::raiseError('ERROR: Ftp configuration file must set all ' .
  51012. 'directory configuration variables. These variables were not set: ' .
  51013. $fail);
  51014. } while (false); // poor man's catch
  51015. unset($this->files['ftp']);
  51016. return PEAR::raiseError('no remote host specified');
  51017. }
  51018. /**
  51019. * Reads the existing configurations and creates the _channels array from it
  51020. */
  51021. function _setupChannels()
  51022. {
  51023. $set = array_flip(array_values($this->_channels));
  51024. foreach ($this->configuration as $layer => $data) {
  51025. $i = 1000;
  51026. if (isset($data['__channels']) && is_array($data['__channels'])) {
  51027. foreach ($data['__channels'] as $channel => $info) {
  51028. $set[$channel] = $i++;
  51029. }
  51030. }
  51031. }
  51032. $this->_channels = array_values(array_flip($set));
  51033. $this->setChannels($this->_channels);
  51034. }
  51035. function deleteChannel($channel)
  51036. {
  51037. $ch = strtolower($channel);
  51038. foreach ($this->configuration as $layer => $data) {
  51039. if (isset($data['__channels']) && isset($data['__channels'][$ch])) {
  51040. unset($this->configuration[$layer]['__channels'][$ch]);
  51041. }
  51042. }
  51043. $this->_channels = array_flip($this->_channels);
  51044. unset($this->_channels[$ch]);
  51045. $this->_channels = array_flip($this->_channels);
  51046. }
  51047. /**
  51048. * Merges data into a config layer from a file. Does the same
  51049. * thing as readConfigFile, except it does not replace all
  51050. * existing values in the config layer.
  51051. * @param string file to read from
  51052. * @param bool whether to overwrite existing data (default TRUE)
  51053. * @param string config layer to insert data into ('user' or 'system')
  51054. * @param string if true, errors are returned if file opening fails
  51055. * @return bool TRUE on success or a PEAR error on failure
  51056. */
  51057. function mergeConfigFile($file, $override = true, $layer = 'user', $strict = true)
  51058. {
  51059. if (empty($this->files[$layer])) {
  51060. return $this->raiseError("unknown config layer `$layer'");
  51061. }
  51062. if ($file === null) {
  51063. $file = $this->files[$layer];
  51064. }
  51065. $data = $this->_readConfigDataFrom($file);
  51066. if (PEAR::isError($data)) {
  51067. if (!$strict) {
  51068. return true;
  51069. }
  51070. $this->_errorsFound++;
  51071. $this->_lastError = $data;
  51072. return $data;
  51073. }
  51074. $this->_decodeInput($data);
  51075. if ($override) {
  51076. $this->configuration[$layer] =
  51077. PEAR_Config::arrayMergeRecursive($this->configuration[$layer], $data);
  51078. } else {
  51079. $this->configuration[$layer] =
  51080. PEAR_Config::arrayMergeRecursive($data, $this->configuration[$layer]);
  51081. }
  51082. $this->_setupChannels();
  51083. if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) {
  51084. $this->_registry[$layer] = new PEAR_Registry(
  51085. $phpdir, false, false,
  51086. $this->get('metadata_dir', $layer, 'pear.php.net'));
  51087. $this->_registry[$layer]->setConfig($this, false);
  51088. $this->_regInitialized[$layer] = false;
  51089. } else {
  51090. unset($this->_registry[$layer]);
  51091. }
  51092. return true;
  51093. }
  51094. /**
  51095. * @param array
  51096. * @param array
  51097. * @return array
  51098. */
  51099. public static function arrayMergeRecursive($arr2, $arr1)
  51100. {
  51101. $ret = array();
  51102. foreach ($arr2 as $key => $data) {
  51103. if (!isset($arr1[$key])) {
  51104. $ret[$key] = $data;
  51105. unset($arr1[$key]);
  51106. continue;
  51107. }
  51108. if (is_array($data)) {
  51109. if (!is_array($arr1[$key])) {
  51110. $ret[$key] = $arr1[$key];
  51111. unset($arr1[$key]);
  51112. continue;
  51113. }
  51114. $ret[$key] = PEAR_Config::arrayMergeRecursive($arr1[$key], $arr2[$key]);
  51115. unset($arr1[$key]);
  51116. }
  51117. }
  51118. return array_merge($ret, $arr1);
  51119. }
  51120. /**
  51121. * Writes data into a config layer from a file.
  51122. *
  51123. * @param string|null file to read from, or null for default
  51124. * @param string config layer to insert data into ('user' or
  51125. * 'system')
  51126. * @param string|null data to write to config file or null for internal data [DEPRECATED]
  51127. * @return bool TRUE on success or a PEAR error on failure
  51128. */
  51129. function writeConfigFile($file = null, $layer = 'user', $data = null)
  51130. {
  51131. $this->_lazyChannelSetup($layer);
  51132. if ($layer == 'both' || $layer == 'all') {
  51133. foreach ($this->files as $type => $file) {
  51134. $err = $this->writeConfigFile($file, $type, $data);
  51135. if (PEAR::isError($err)) {
  51136. return $err;
  51137. }
  51138. }
  51139. return true;
  51140. }
  51141. if (empty($this->files[$layer])) {
  51142. return $this->raiseError("unknown config file type `$layer'");
  51143. }
  51144. if ($file === null) {
  51145. $file = $this->files[$layer];
  51146. }
  51147. $data = ($data === null) ? $this->configuration[$layer] : $data;
  51148. $this->_encodeOutput($data);
  51149. $opt = array('-p', dirname($file));
  51150. if (!@System::mkDir($opt)) {
  51151. return $this->raiseError("could not create directory: " . dirname($file));
  51152. }
  51153. if (file_exists($file) && is_file($file) && !is_writeable($file)) {
  51154. return $this->raiseError("no write access to $file!");
  51155. }
  51156. $fp = @fopen($file, "w");
  51157. if (!$fp) {
  51158. return $this->raiseError("PEAR_Config::writeConfigFile fopen('$file','w') failed ($php_errormsg)");
  51159. }
  51160. $contents = "#PEAR_Config 0.9\n" . serialize($data);
  51161. if (!@fwrite($fp, $contents)) {
  51162. return $this->raiseError("PEAR_Config::writeConfigFile: fwrite failed ($php_errormsg)");
  51163. }
  51164. return true;
  51165. }
  51166. /**
  51167. * Reads configuration data from a file and returns the parsed data
  51168. * in an array.
  51169. *
  51170. * @param string file to read from
  51171. * @return array configuration data or a PEAR error on failure
  51172. * @access private
  51173. */
  51174. function _readConfigDataFrom($file)
  51175. {
  51176. $fp = false;
  51177. if (file_exists($file)) {
  51178. $fp = @fopen($file, "r");
  51179. }
  51180. if (!$fp) {
  51181. return $this->raiseError("PEAR_Config::readConfigFile fopen('$file','r') failed");
  51182. }
  51183. $size = filesize($file);
  51184. fclose($fp);
  51185. $contents = file_get_contents($file);
  51186. if (empty($contents)) {
  51187. return $this->raiseError('Configuration file "' . $file . '" is empty');
  51188. }
  51189. $version = false;
  51190. if (preg_match('/^#PEAR_Config\s+(\S+)\s+/si', $contents, $matches)) {
  51191. $version = $matches[1];
  51192. $contents = substr($contents, strlen($matches[0]));
  51193. } else {
  51194. // Museum config file
  51195. if (substr($contents,0,2) == 'a:') {
  51196. $version = '0.1';
  51197. }
  51198. }
  51199. if ($version && version_compare("$version", '1', '<')) {
  51200. $data = @unserialize($contents);
  51201. if (!is_array($data) && !$data) {
  51202. if ($contents == serialize(false)) {
  51203. $data = array();
  51204. } else {
  51205. $err = $this->raiseError("PEAR_Config: bad data in $file");
  51206. return $err;
  51207. }
  51208. }
  51209. if (!is_array($data)) {
  51210. if (strlen(trim($contents)) > 0) {
  51211. $error = "PEAR_Config: bad data in $file";
  51212. $err = $this->raiseError($error);
  51213. return $err;
  51214. }
  51215. $data = array();
  51216. }
  51217. // add parsing of newer formats here...
  51218. } else {
  51219. $err = $this->raiseError("$file: unknown version `$version'");
  51220. return $err;
  51221. }
  51222. return $data;
  51223. }
  51224. /**
  51225. * Gets the file used for storing the config for a layer
  51226. *
  51227. * @param string $layer 'user' or 'system'
  51228. */
  51229. function getConfFile($layer)
  51230. {
  51231. return $this->files[$layer];
  51232. }
  51233. /**
  51234. * @param string Configuration class name, used for detecting duplicate calls
  51235. * @param array information on a role as parsed from its xml file
  51236. * @return true|PEAR_Error
  51237. * @access private
  51238. */
  51239. function _addConfigVars($class, $vars)
  51240. {
  51241. static $called = array();
  51242. if (isset($called[$class])) {
  51243. return;
  51244. }
  51245. $called[$class] = 1;
  51246. if (count($vars) > 3) {
  51247. return $this->raiseError('Roles can only define 3 new config variables or less');
  51248. }
  51249. foreach ($vars as $name => $var) {
  51250. if (!is_array($var)) {
  51251. return $this->raiseError('Configuration information must be an array');
  51252. }
  51253. if (!isset($var['type'])) {
  51254. return $this->raiseError('Configuration information must contain a type');
  51255. } elseif (!in_array($var['type'],
  51256. array('string', 'mask', 'password', 'directory', 'file', 'set'))) {
  51257. return $this->raiseError(
  51258. 'Configuration type must be one of directory, file, string, ' .
  51259. 'mask, set, or password');
  51260. }
  51261. if (!isset($var['default'])) {
  51262. return $this->raiseError(
  51263. 'Configuration information must contain a default value ("default" index)');
  51264. }
  51265. if (is_array($var['default'])) {
  51266. $real_default = '';
  51267. foreach ($var['default'] as $config_var => $val) {
  51268. if (strpos($config_var, 'text') === 0) {
  51269. $real_default .= $val;
  51270. } elseif (strpos($config_var, 'constant') === 0) {
  51271. if (!defined($val)) {
  51272. return $this->raiseError(
  51273. 'Unknown constant "' . $val . '" requested in ' .
  51274. 'default value for configuration variable "' .
  51275. $name . '"');
  51276. }
  51277. $real_default .= constant($val);
  51278. } elseif (isset($this->configuration_info[$config_var])) {
  51279. $real_default .=
  51280. $this->configuration_info[$config_var]['default'];
  51281. } else {
  51282. return $this->raiseError(
  51283. 'Unknown request for "' . $config_var . '" value in ' .
  51284. 'default value for configuration variable "' .
  51285. $name . '"');
  51286. }
  51287. }
  51288. $var['default'] = $real_default;
  51289. }
  51290. if ($var['type'] == 'integer') {
  51291. $var['default'] = (integer) $var['default'];
  51292. }
  51293. if (!isset($var['doc'])) {
  51294. return $this->raiseError(
  51295. 'Configuration information must contain a summary ("doc" index)');
  51296. }
  51297. if (!isset($var['prompt'])) {
  51298. return $this->raiseError(
  51299. 'Configuration information must contain a simple prompt ("prompt" index)');
  51300. }
  51301. if (!isset($var['group'])) {
  51302. return $this->raiseError(
  51303. 'Configuration information must contain a simple group ("group" index)');
  51304. }
  51305. if (isset($this->configuration_info[$name])) {
  51306. return $this->raiseError('Configuration variable "' . $name .
  51307. '" already exists');
  51308. }
  51309. $this->configuration_info[$name] = $var;
  51310. // fix bug #7351: setting custom config variable in a channel fails
  51311. $this->_channelConfigInfo[] = $name;
  51312. }
  51313. return true;
  51314. }
  51315. /**
  51316. * Encodes/scrambles configuration data before writing to files.
  51317. * Currently, 'password' values will be base64-encoded as to avoid
  51318. * that people spot cleartext passwords by accident.
  51319. *
  51320. * @param array (reference) array to encode values in
  51321. * @return bool TRUE on success
  51322. * @access private
  51323. */
  51324. function _encodeOutput(&$data)
  51325. {
  51326. foreach ($data as $key => $value) {
  51327. if ($key == '__channels') {
  51328. foreach ($data['__channels'] as $channel => $blah) {
  51329. $this->_encodeOutput($data['__channels'][$channel]);
  51330. }
  51331. }
  51332. if (!isset($this->configuration_info[$key])) {
  51333. continue;
  51334. }
  51335. $type = $this->configuration_info[$key]['type'];
  51336. switch ($type) {
  51337. // we base64-encode passwords so they are at least
  51338. // not shown in plain by accident
  51339. case 'password': {
  51340. $data[$key] = base64_encode($data[$key]);
  51341. break;
  51342. }
  51343. case 'mask': {
  51344. $data[$key] = octdec($data[$key]);
  51345. break;
  51346. }
  51347. }
  51348. }
  51349. return true;
  51350. }
  51351. /**
  51352. * Decodes/unscrambles configuration data after reading from files.
  51353. *
  51354. * @param array (reference) array to encode values in
  51355. * @return bool TRUE on success
  51356. * @access private
  51357. *
  51358. * @see PEAR_Config::_encodeOutput
  51359. */
  51360. function _decodeInput(&$data)
  51361. {
  51362. if (!is_array($data)) {
  51363. return true;
  51364. }
  51365. foreach ($data as $key => $value) {
  51366. if ($key == '__channels') {
  51367. foreach ($data['__channels'] as $channel => $blah) {
  51368. $this->_decodeInput($data['__channels'][$channel]);
  51369. }
  51370. }
  51371. if (!isset($this->configuration_info[$key])) {
  51372. continue;
  51373. }
  51374. $type = $this->configuration_info[$key]['type'];
  51375. switch ($type) {
  51376. case 'password': {
  51377. $data[$key] = base64_decode($data[$key]);
  51378. break;
  51379. }
  51380. case 'mask': {
  51381. $data[$key] = decoct($data[$key]);
  51382. break;
  51383. }
  51384. }
  51385. }
  51386. return true;
  51387. }
  51388. /**
  51389. * Retrieve the default channel.
  51390. *
  51391. * On startup, channels are not initialized, so if the default channel is not
  51392. * pear.php.net, then initialize the config.
  51393. * @param string registry layer
  51394. * @return string|false
  51395. */
  51396. function getDefaultChannel($layer = null)
  51397. {
  51398. $ret = false;
  51399. if ($layer === null) {
  51400. foreach ($this->layers as $layer) {
  51401. if (isset($this->configuration[$layer]['default_channel'])) {
  51402. $ret = $this->configuration[$layer]['default_channel'];
  51403. break;
  51404. }
  51405. }
  51406. } elseif (isset($this->configuration[$layer]['default_channel'])) {
  51407. $ret = $this->configuration[$layer]['default_channel'];
  51408. }
  51409. if ($ret == 'pear.php.net' && defined('PEAR_RUNTYPE') && PEAR_RUNTYPE == 'pecl') {
  51410. $ret = 'pecl.php.net';
  51411. }
  51412. if ($ret) {
  51413. if ($ret != 'pear.php.net') {
  51414. $this->_lazyChannelSetup();
  51415. }
  51416. return $ret;
  51417. }
  51418. return PEAR_CONFIG_DEFAULT_CHANNEL;
  51419. }
  51420. /**
  51421. * Returns a configuration value, prioritizing layers as per the
  51422. * layers property.
  51423. *
  51424. * @param string config key
  51425. * @return mixed the config value, or NULL if not found
  51426. * @access public
  51427. */
  51428. function get($key, $layer = null, $channel = false)
  51429. {
  51430. if (!isset($this->configuration_info[$key])) {
  51431. return null;
  51432. }
  51433. if ($key == '__channels') {
  51434. return null;
  51435. }
  51436. if ($key == 'default_channel') {
  51437. return $this->getDefaultChannel($layer);
  51438. }
  51439. if (!$channel) {
  51440. $channel = $this->getDefaultChannel();
  51441. } elseif ($channel != 'pear.php.net') {
  51442. $this->_lazyChannelSetup();
  51443. }
  51444. $channel = strtolower($channel);
  51445. $test = (in_array($key, $this->_channelConfigInfo)) ?
  51446. $this->_getChannelValue($key, $layer, $channel) :
  51447. null;
  51448. if ($test !== null) {
  51449. if ($this->_installRoot) {
  51450. if (in_array($this->getGroup($key),
  51451. array('File Locations', 'File Locations (Advanced)')) &&
  51452. $this->getType($key) == 'directory') {
  51453. return $this->_prependPath($test, $this->_installRoot);
  51454. }
  51455. }
  51456. return $test;
  51457. }
  51458. if ($layer === null) {
  51459. foreach ($this->layers as $layer) {
  51460. if (isset($this->configuration[$layer][$key])) {
  51461. $test = $this->configuration[$layer][$key];
  51462. if ($this->_installRoot) {
  51463. if (in_array($this->getGroup($key),
  51464. array('File Locations', 'File Locations (Advanced)')) &&
  51465. $this->getType($key) == 'directory') {
  51466. return $this->_prependPath($test, $this->_installRoot);
  51467. }
  51468. }
  51469. if ($key == 'preferred_mirror') {
  51470. $reg = &$this->getRegistry();
  51471. if (is_object($reg)) {
  51472. $chan = $reg->getChannel($channel);
  51473. if (PEAR::isError($chan)) {
  51474. return $channel;
  51475. }
  51476. if (!$chan->getMirror($test) && $chan->getName() != $test) {
  51477. return $channel; // mirror does not exist
  51478. }
  51479. }
  51480. }
  51481. return $test;
  51482. }
  51483. }
  51484. } elseif (isset($this->configuration[$layer][$key])) {
  51485. $test = $this->configuration[$layer][$key];
  51486. if ($this->_installRoot) {
  51487. if (in_array($this->getGroup($key),
  51488. array('File Locations', 'File Locations (Advanced)')) &&
  51489. $this->getType($key) == 'directory') {
  51490. return $this->_prependPath($test, $this->_installRoot);
  51491. }
  51492. }
  51493. if ($key == 'preferred_mirror') {
  51494. $reg = &$this->getRegistry();
  51495. if (is_object($reg)) {
  51496. $chan = $reg->getChannel($channel);
  51497. if (PEAR::isError($chan)) {
  51498. return $channel;
  51499. }
  51500. if (!$chan->getMirror($test) && $chan->getName() != $test) {
  51501. return $channel; // mirror does not exist
  51502. }
  51503. }
  51504. }
  51505. return $test;
  51506. }
  51507. return null;
  51508. }
  51509. /**
  51510. * Returns a channel-specific configuration value, prioritizing layers as per the
  51511. * layers property.
  51512. *
  51513. * @param string config key
  51514. * @return mixed the config value, or NULL if not found
  51515. * @access private
  51516. */
  51517. function _getChannelValue($key, $layer, $channel)
  51518. {
  51519. if ($key == '__channels' || $channel == 'pear.php.net') {
  51520. return null;
  51521. }
  51522. $ret = null;
  51523. if ($layer === null) {
  51524. foreach ($this->layers as $ilayer) {
  51525. if (isset($this->configuration[$ilayer]['__channels'][$channel][$key])) {
  51526. $ret = $this->configuration[$ilayer]['__channels'][$channel][$key];
  51527. break;
  51528. }
  51529. }
  51530. } elseif (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
  51531. $ret = $this->configuration[$layer]['__channels'][$channel][$key];
  51532. }
  51533. if ($key != 'preferred_mirror') {
  51534. return $ret;
  51535. }
  51536. if ($ret !== null) {
  51537. $reg = &$this->getRegistry($layer);
  51538. if (is_object($reg)) {
  51539. $chan = $reg->getChannel($channel);
  51540. if (PEAR::isError($chan)) {
  51541. return $channel;
  51542. }
  51543. if (!$chan->getMirror($ret) && $chan->getName() != $ret) {
  51544. return $channel; // mirror does not exist
  51545. }
  51546. }
  51547. return $ret;
  51548. }
  51549. if ($channel != $this->getDefaultChannel($layer)) {
  51550. return $channel; // we must use the channel name as the preferred mirror
  51551. // if the user has not chosen an alternate
  51552. }
  51553. return $this->getDefaultChannel($layer);
  51554. }
  51555. /**
  51556. * Set a config value in a specific layer (defaults to 'user').
  51557. * Enforces the types defined in the configuration_info array. An
  51558. * integer config variable will be cast to int, and a set config
  51559. * variable will be validated against its legal values.
  51560. *
  51561. * @param string config key
  51562. * @param string config value
  51563. * @param string (optional) config layer
  51564. * @param string channel to set this value for, or null for global value
  51565. * @return bool TRUE on success, FALSE on failure
  51566. */
  51567. function set($key, $value, $layer = 'user', $channel = false)
  51568. {
  51569. if ($key == '__channels') {
  51570. return false;
  51571. }
  51572. if (!isset($this->configuration[$layer])) {
  51573. return false;
  51574. }
  51575. if ($key == 'default_channel') {
  51576. // can only set this value globally
  51577. $channel = 'pear.php.net';
  51578. if ($value != 'pear.php.net') {
  51579. $this->_lazyChannelSetup($layer);
  51580. }
  51581. }
  51582. if ($key == 'preferred_mirror') {
  51583. if ($channel == '__uri') {
  51584. return false; // can't set the __uri pseudo-channel's mirror
  51585. }
  51586. $reg = &$this->getRegistry($layer);
  51587. if (is_object($reg)) {
  51588. $chan = $reg->getChannel($channel ? $channel : 'pear.php.net');
  51589. if (PEAR::isError($chan)) {
  51590. return false;
  51591. }
  51592. if (!$chan->getMirror($value) && $chan->getName() != $value) {
  51593. return false; // mirror does not exist
  51594. }
  51595. }
  51596. }
  51597. if (!isset($this->configuration_info[$key])) {
  51598. return false;
  51599. }
  51600. extract($this->configuration_info[$key]);
  51601. switch ($type) {
  51602. case 'integer':
  51603. $value = (int)$value;
  51604. break;
  51605. case 'set': {
  51606. // If a valid_set is specified, require the value to
  51607. // be in the set. If there is no valid_set, accept
  51608. // any value.
  51609. if ($valid_set) {
  51610. reset($valid_set);
  51611. if ((key($valid_set) === 0 && !in_array($value, $valid_set)) ||
  51612. (key($valid_set) !== 0 && empty($valid_set[$value])))
  51613. {
  51614. return false;
  51615. }
  51616. }
  51617. break;
  51618. }
  51619. }
  51620. if (!$channel) {
  51621. $channel = $this->get('default_channel', null, 'pear.php.net');
  51622. }
  51623. if (!in_array($channel, $this->_channels)) {
  51624. $this->_lazyChannelSetup($layer);
  51625. $reg = &$this->getRegistry($layer);
  51626. if ($reg) {
  51627. $channel = $reg->channelName($channel);
  51628. }
  51629. if (!in_array($channel, $this->_channels)) {
  51630. return false;
  51631. }
  51632. }
  51633. if ($channel != 'pear.php.net') {
  51634. if (in_array($key, $this->_channelConfigInfo)) {
  51635. $this->configuration[$layer]['__channels'][$channel][$key] = $value;
  51636. return true;
  51637. }
  51638. return false;
  51639. }
  51640. if ($key == 'default_channel') {
  51641. if (!isset($reg)) {
  51642. $reg = &$this->getRegistry($layer);
  51643. if (!$reg) {
  51644. $reg = &$this->getRegistry();
  51645. }
  51646. }
  51647. if ($reg) {
  51648. $value = $reg->channelName($value);
  51649. }
  51650. if (!$value) {
  51651. return false;
  51652. }
  51653. }
  51654. $this->configuration[$layer][$key] = $value;
  51655. if ($key == 'php_dir' && !$this->_noRegistry) {
  51656. if (!isset($this->_registry[$layer]) ||
  51657. $value != $this->_registry[$layer]->install_dir) {
  51658. $this->_registry[$layer] = new PEAR_Registry($value);
  51659. $this->_regInitialized[$layer] = false;
  51660. $this->_registry[$layer]->setConfig($this, false);
  51661. }
  51662. }
  51663. return true;
  51664. }
  51665. function _lazyChannelSetup($uselayer = false)
  51666. {
  51667. if ($this->_noRegistry) {
  51668. return;
  51669. }
  51670. $merge = false;
  51671. foreach ($this->_registry as $layer => $p) {
  51672. if ($uselayer && $uselayer != $layer) {
  51673. continue;
  51674. }
  51675. if (!$this->_regInitialized[$layer]) {
  51676. if ($layer == 'default' && isset($this->_registry['user']) ||
  51677. isset($this->_registry['system'])) {
  51678. // only use the default registry if there are no alternatives
  51679. continue;
  51680. }
  51681. if (!is_object($this->_registry[$layer])) {
  51682. if ($phpdir = $this->get('php_dir', $layer, 'pear.php.net')) {
  51683. $this->_registry[$layer] = new PEAR_Registry(
  51684. $phpdir, false, false,
  51685. $this->get('metadata_dir', $layer, 'pear.php.net'));
  51686. $this->_registry[$layer]->setConfig($this, false);
  51687. $this->_regInitialized[$layer] = false;
  51688. } else {
  51689. unset($this->_registry[$layer]);
  51690. return;
  51691. }
  51692. }
  51693. $this->setChannels($this->_registry[$layer]->listChannels(), $merge);
  51694. $this->_regInitialized[$layer] = true;
  51695. $merge = true;
  51696. }
  51697. }
  51698. }
  51699. /**
  51700. * Set the list of channels.
  51701. *
  51702. * This should be set via a call to {@link PEAR_Registry::listChannels()}
  51703. * @param array
  51704. * @param bool
  51705. * @return bool success of operation
  51706. */
  51707. function setChannels($channels, $merge = false)
  51708. {
  51709. if (!is_array($channels)) {
  51710. return false;
  51711. }
  51712. if ($merge) {
  51713. $this->_channels = array_merge($this->_channels, $channels);
  51714. } else {
  51715. $this->_channels = $channels;
  51716. }
  51717. foreach ($channels as $channel) {
  51718. $channel = strtolower($channel);
  51719. if ($channel == 'pear.php.net') {
  51720. continue;
  51721. }
  51722. foreach ($this->layers as $layer) {
  51723. if (!isset($this->configuration[$layer]['__channels'])) {
  51724. $this->configuration[$layer]['__channels'] = array();
  51725. }
  51726. if (!isset($this->configuration[$layer]['__channels'][$channel])
  51727. || !is_array($this->configuration[$layer]['__channels'][$channel])) {
  51728. $this->configuration[$layer]['__channels'][$channel] = array();
  51729. }
  51730. }
  51731. }
  51732. return true;
  51733. }
  51734. /**
  51735. * Get the type of a config value.
  51736. *
  51737. * @param string config key
  51738. *
  51739. * @return string type, one of "string", "integer", "file",
  51740. * "directory", "set" or "password".
  51741. *
  51742. * @access public
  51743. *
  51744. */
  51745. function getType($key)
  51746. {
  51747. if (isset($this->configuration_info[$key])) {
  51748. return $this->configuration_info[$key]['type'];
  51749. }
  51750. return false;
  51751. }
  51752. /**
  51753. * Get the documentation for a config value.
  51754. *
  51755. * @param string config key
  51756. * @return string documentation string
  51757. *
  51758. * @access public
  51759. *
  51760. */
  51761. function getDocs($key)
  51762. {
  51763. if (isset($this->configuration_info[$key])) {
  51764. return $this->configuration_info[$key]['doc'];
  51765. }
  51766. return false;
  51767. }
  51768. /**
  51769. * Get the short documentation for a config value.
  51770. *
  51771. * @param string config key
  51772. * @return string short documentation string
  51773. *
  51774. * @access public
  51775. *
  51776. */
  51777. function getPrompt($key)
  51778. {
  51779. if (isset($this->configuration_info[$key])) {
  51780. return $this->configuration_info[$key]['prompt'];
  51781. }
  51782. return false;
  51783. }
  51784. /**
  51785. * Get the parameter group for a config key.
  51786. *
  51787. * @param string config key
  51788. * @return string parameter group
  51789. *
  51790. * @access public
  51791. *
  51792. */
  51793. function getGroup($key)
  51794. {
  51795. if (isset($this->configuration_info[$key])) {
  51796. return $this->configuration_info[$key]['group'];
  51797. }
  51798. return false;
  51799. }
  51800. /**
  51801. * Get the list of parameter groups.
  51802. *
  51803. * @return array list of parameter groups
  51804. *
  51805. * @access public
  51806. *
  51807. */
  51808. function getGroups()
  51809. {
  51810. $tmp = array();
  51811. foreach ($this->configuration_info as $key => $info) {
  51812. $tmp[$info['group']] = 1;
  51813. }
  51814. return array_keys($tmp);
  51815. }
  51816. /**
  51817. * Get the list of the parameters in a group.
  51818. *
  51819. * @param string $group parameter group
  51820. * @return array list of parameters in $group
  51821. *
  51822. * @access public
  51823. *
  51824. */
  51825. function getGroupKeys($group)
  51826. {
  51827. $keys = array();
  51828. foreach ($this->configuration_info as $key => $info) {
  51829. if ($info['group'] == $group) {
  51830. $keys[] = $key;
  51831. }
  51832. }
  51833. return $keys;
  51834. }
  51835. /**
  51836. * Get the list of allowed set values for a config value. Returns
  51837. * NULL for config values that are not sets.
  51838. *
  51839. * @param string config key
  51840. * @return array enumerated array of set values, or NULL if the
  51841. * config key is unknown or not a set
  51842. *
  51843. * @access public
  51844. *
  51845. */
  51846. function getSetValues($key)
  51847. {
  51848. if (isset($this->configuration_info[$key]) &&
  51849. isset($this->configuration_info[$key]['type']) &&
  51850. $this->configuration_info[$key]['type'] == 'set')
  51851. {
  51852. $valid_set = $this->configuration_info[$key]['valid_set'];
  51853. reset($valid_set);
  51854. if (key($valid_set) === 0) {
  51855. return $valid_set;
  51856. }
  51857. return array_keys($valid_set);
  51858. }
  51859. return null;
  51860. }
  51861. /**
  51862. * Get all the current config keys.
  51863. *
  51864. * @return array simple array of config keys
  51865. *
  51866. * @access public
  51867. */
  51868. function getKeys()
  51869. {
  51870. $keys = array();
  51871. foreach ($this->layers as $layer) {
  51872. $test = $this->configuration[$layer];
  51873. if (isset($test['__channels'])) {
  51874. foreach ($test['__channels'] as $channel => $configs) {
  51875. $keys = array_merge($keys, $configs);
  51876. }
  51877. }
  51878. unset($test['__channels']);
  51879. $keys = array_merge($keys, $test);
  51880. }
  51881. return array_keys($keys);
  51882. }
  51883. /**
  51884. * Remove the a config key from a specific config layer.
  51885. *
  51886. * @param string config key
  51887. * @param string (optional) config layer
  51888. * @param string (optional) channel (defaults to default channel)
  51889. * @return bool TRUE on success, FALSE on failure
  51890. *
  51891. * @access public
  51892. */
  51893. function remove($key, $layer = 'user', $channel = null)
  51894. {
  51895. if ($channel === null) {
  51896. $channel = $this->getDefaultChannel();
  51897. }
  51898. if ($channel !== 'pear.php.net') {
  51899. if (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
  51900. unset($this->configuration[$layer]['__channels'][$channel][$key]);
  51901. return true;
  51902. }
  51903. }
  51904. if (isset($this->configuration[$layer][$key])) {
  51905. unset($this->configuration[$layer][$key]);
  51906. return true;
  51907. }
  51908. return false;
  51909. }
  51910. /**
  51911. * Temporarily remove an entire config layer. USE WITH CARE!
  51912. *
  51913. * @param string config key
  51914. * @param string (optional) config layer
  51915. * @return bool TRUE on success, FALSE on failure
  51916. *
  51917. * @access public
  51918. */
  51919. function removeLayer($layer)
  51920. {
  51921. if (isset($this->configuration[$layer])) {
  51922. $this->configuration[$layer] = array();
  51923. return true;
  51924. }
  51925. return false;
  51926. }
  51927. /**
  51928. * Stores configuration data in a layer.
  51929. *
  51930. * @param string config layer to store
  51931. * @return bool TRUE on success, or PEAR error on failure
  51932. *
  51933. * @access public
  51934. */
  51935. function store($layer = 'user', $data = null)
  51936. {
  51937. return $this->writeConfigFile(null, $layer, $data);
  51938. }
  51939. /**
  51940. * Tells what config layer that gets to define a key.
  51941. *
  51942. * @param string config key
  51943. * @param boolean return the defining channel
  51944. *
  51945. * @return string|array the config layer, or an empty string if not found.
  51946. *
  51947. * if $returnchannel, the return is an array array('layer' => layername,
  51948. * 'channel' => channelname), or an empty string if not found
  51949. *
  51950. * @access public
  51951. */
  51952. function definedBy($key, $returnchannel = false)
  51953. {
  51954. foreach ($this->layers as $layer) {
  51955. $channel = $this->getDefaultChannel();
  51956. if ($channel !== 'pear.php.net') {
  51957. if (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
  51958. if ($returnchannel) {
  51959. return array('layer' => $layer, 'channel' => $channel);
  51960. }
  51961. return $layer;
  51962. }
  51963. }
  51964. if (isset($this->configuration[$layer][$key])) {
  51965. if ($returnchannel) {
  51966. return array('layer' => $layer, 'channel' => 'pear.php.net');
  51967. }
  51968. return $layer;
  51969. }
  51970. }
  51971. return '';
  51972. }
  51973. /**
  51974. * Tells whether a given key exists as a config value.
  51975. *
  51976. * @param string config key
  51977. * @return bool whether <config key> exists in this object
  51978. *
  51979. * @access public
  51980. */
  51981. function isDefined($key)
  51982. {
  51983. foreach ($this->layers as $layer) {
  51984. if (isset($this->configuration[$layer][$key])) {
  51985. return true;
  51986. }
  51987. }
  51988. return false;
  51989. }
  51990. /**
  51991. * Tells whether a given config layer exists.
  51992. *
  51993. * @param string config layer
  51994. * @return bool whether <config layer> exists in this object
  51995. *
  51996. * @access public
  51997. */
  51998. function isDefinedLayer($layer)
  51999. {
  52000. return isset($this->configuration[$layer]);
  52001. }
  52002. /**
  52003. * Returns the layers defined (except the 'default' one)
  52004. *
  52005. * @return array of the defined layers
  52006. */
  52007. function getLayers()
  52008. {
  52009. $cf = $this->configuration;
  52010. unset($cf['default']);
  52011. return array_keys($cf);
  52012. }
  52013. function apiVersion()
  52014. {
  52015. return '1.1';
  52016. }
  52017. /**
  52018. * @return PEAR_Registry
  52019. */
  52020. function &getRegistry($use = null)
  52021. {
  52022. $layer = $use === null ? 'user' : $use;
  52023. if (isset($this->_registry[$layer])) {
  52024. return $this->_registry[$layer];
  52025. } elseif ($use === null && isset($this->_registry['system'])) {
  52026. return $this->_registry['system'];
  52027. } elseif ($use === null && isset($this->_registry['default'])) {
  52028. return $this->_registry['default'];
  52029. } elseif ($use) {
  52030. $a = false;
  52031. return $a;
  52032. }
  52033. // only go here if null was passed in
  52034. echo "CRITICAL ERROR: Registry could not be initialized from any value";
  52035. exit(1);
  52036. }
  52037. /**
  52038. * This is to allow customization like the use of installroot
  52039. * @param PEAR_Registry
  52040. * @return bool
  52041. */
  52042. function setRegistry(&$reg, $layer = 'user')
  52043. {
  52044. if ($this->_noRegistry) {
  52045. return false;
  52046. }
  52047. if (!in_array($layer, array('user', 'system'))) {
  52048. return false;
  52049. }
  52050. $this->_registry[$layer] = &$reg;
  52051. if (is_object($reg)) {
  52052. $this->_registry[$layer]->setConfig($this, false);
  52053. }
  52054. return true;
  52055. }
  52056. function noRegistry()
  52057. {
  52058. $this->_noRegistry = true;
  52059. }
  52060. /**
  52061. * @return PEAR_REST
  52062. */
  52063. function &getREST($version, $options = array())
  52064. {
  52065. $version = str_replace('.', '', $version);
  52066. if (!class_exists($class = 'PEAR_REST_' . $version)) {
  52067. require_once 'PEAR/REST/' . $version . '.php';
  52068. }
  52069. $remote = new $class($this, $options);
  52070. return $remote;
  52071. }
  52072. /**
  52073. * The ftp server is set in {@link readFTPConfigFile()}. It exists only if a
  52074. * remote configuration file has been specified
  52075. * @return PEAR_FTP|false
  52076. */
  52077. function &getFTP()
  52078. {
  52079. if (isset($this->_ftp)) {
  52080. return $this->_ftp;
  52081. }
  52082. $a = false;
  52083. return $a;
  52084. }
  52085. static function _prependPath($path, $prepend)
  52086. {
  52087. if (strlen($prepend) > 0) {
  52088. if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
  52089. if (preg_match('/^[a-z]:/i', $prepend)) {
  52090. $prepend = substr($prepend, 2);
  52091. } elseif ($prepend[0] != '\\') {
  52092. $prepend = "\\$prepend";
  52093. }
  52094. $path = substr($path, 0, 2) . $prepend . substr($path, 2);
  52095. } else {
  52096. $path = $prepend . $path;
  52097. }
  52098. }
  52099. return $path;
  52100. }
  52101. /**
  52102. * @param string|false installation directory to prepend to all _dir variables, or false to
  52103. * disable
  52104. */
  52105. function setInstallRoot($root)
  52106. {
  52107. if (substr($root, -1) == DIRECTORY_SEPARATOR) {
  52108. $root = substr($root, 0, -1);
  52109. }
  52110. $old = $this->_installRoot;
  52111. $this->_installRoot = $root;
  52112. if (($old != $root) && !$this->_noRegistry) {
  52113. foreach (array_keys($this->_registry) as $layer) {
  52114. if ($layer == 'ftp' || !isset($this->_registry[$layer])) {
  52115. continue;
  52116. }
  52117. $this->_registry[$layer] =
  52118. new PEAR_Registry(
  52119. $this->get('php_dir', $layer, 'pear.php.net'), false, false,
  52120. $this->get('metadata_dir', $layer, 'pear.php.net'));
  52121. $this->_registry[$layer]->setConfig($this, false);
  52122. $this->_regInitialized[$layer] = false;
  52123. }
  52124. }
  52125. }
  52126. }
  52127. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/DependencyDB.php������������������������������������������������������������������0000664�0001750�0001750�00000057401�14720722517�016023� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  52128. /**
  52129. * PEAR_DependencyDB, advanced installed packages dependency database
  52130. *
  52131. * PHP versions 4 and 5
  52132. *
  52133. * @category pear
  52134. * @package PEAR
  52135. * @author Tomas V. V. Cox <cox@idecnet.com>
  52136. * @author Greg Beaver <cellog@php.net>
  52137. * @copyright 1997-2009 The Authors
  52138. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  52139. * @link http://pear.php.net/package/PEAR
  52140. * @since File available since Release 1.4.0a1
  52141. */
  52142. /**
  52143. * Needed for error handling
  52144. */
  52145. require_once 'PEAR.php';
  52146. require_once 'PEAR/Config.php';
  52147. $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] = array();
  52148. /**
  52149. * Track dependency relationships between installed packages
  52150. * @category pear
  52151. * @package PEAR
  52152. * @author Greg Beaver <cellog@php.net>
  52153. * @author Tomas V.V.Cox <cox@idec.net.com>
  52154. * @copyright 1997-2009 The Authors
  52155. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  52156. * @version Release: 1.10.16
  52157. * @link http://pear.php.net/package/PEAR
  52158. * @since Class available since Release 1.4.0a1
  52159. */
  52160. class PEAR_DependencyDB
  52161. {
  52162. // {{{ properties
  52163. /**
  52164. * This is initialized by {@link setConfig()}
  52165. * @var PEAR_Config
  52166. * @access private
  52167. */
  52168. var $_config;
  52169. /**
  52170. * This is initialized by {@link setConfig()}
  52171. * @var PEAR_Registry
  52172. * @access private
  52173. */
  52174. var $_registry;
  52175. /**
  52176. * Filename of the dependency DB (usually .depdb)
  52177. * @var string
  52178. * @access private
  52179. */
  52180. var $_depdb = false;
  52181. /**
  52182. * File name of the lockfile (usually .depdblock)
  52183. * @var string
  52184. * @access private
  52185. */
  52186. var $_lockfile = false;
  52187. /**
  52188. * Open file resource for locking the lockfile
  52189. * @var resource|false
  52190. * @access private
  52191. */
  52192. var $_lockFp = false;
  52193. /**
  52194. * API version of this class, used to validate a file on-disk
  52195. * @var string
  52196. * @access private
  52197. */
  52198. var $_version = '1.0';
  52199. /**
  52200. * Cached dependency database file
  52201. * @var array|null
  52202. * @access private
  52203. */
  52204. var $_cache;
  52205. // }}}
  52206. // {{{ & singleton()
  52207. /**
  52208. * Get a raw dependency database. Calls setConfig() and assertDepsDB()
  52209. * @param PEAR_Config
  52210. * @param string|false full path to the dependency database, or false to use default
  52211. * @return PEAR_DependencyDB|PEAR_Error
  52212. */
  52213. public static function &singleton(&$config, $depdb = false)
  52214. {
  52215. $phpdir = $config->get('php_dir', null, 'pear.php.net');
  52216. if (!isset($GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir])) {
  52217. $a = new PEAR_DependencyDB;
  52218. $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir] = &$a;
  52219. $a->setConfig($config, $depdb);
  52220. $e = $a->assertDepsDB();
  52221. if (PEAR::isError($e)) {
  52222. return $e;
  52223. }
  52224. }
  52225. return $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir];
  52226. }
  52227. /**
  52228. * Set up the registry/location of dependency DB
  52229. * @param PEAR_Config|false
  52230. * @param string|false full path to the dependency database, or false to use default
  52231. */
  52232. function setConfig(&$config, $depdb = false)
  52233. {
  52234. if (!$config) {
  52235. $this->_config = &PEAR_Config::singleton();
  52236. } else {
  52237. $this->_config = &$config;
  52238. }
  52239. $this->_registry = &$this->_config->getRegistry();
  52240. if (!$depdb) {
  52241. $dir = $this->_config->get('metadata_dir', null, 'pear.php.net');
  52242. if (!$dir) {
  52243. $dir = $this->_config->get('php_dir', null, 'pear.php.net');
  52244. }
  52245. $this->_depdb = $dir . DIRECTORY_SEPARATOR . '.depdb';
  52246. } else {
  52247. $this->_depdb = $depdb;
  52248. }
  52249. $this->_lockfile = dirname($this->_depdb) . DIRECTORY_SEPARATOR . '.depdblock';
  52250. }
  52251. // }}}
  52252. function hasWriteAccess()
  52253. {
  52254. if (!file_exists($this->_depdb)) {
  52255. $dir = $this->_depdb;
  52256. while ($dir && $dir != '.') {
  52257. $dir = dirname($dir); // cd ..
  52258. if ($dir != '.' && file_exists($dir)) {
  52259. if (is_writeable($dir)) {
  52260. return true;
  52261. }
  52262. return false;
  52263. }
  52264. }
  52265. return false;
  52266. }
  52267. return is_writeable($this->_depdb);
  52268. }
  52269. // {{{ assertDepsDB()
  52270. /**
  52271. * Create the dependency database, if it doesn't exist. Error if the database is
  52272. * newer than the code reading it.
  52273. * @return void|PEAR_Error
  52274. */
  52275. function assertDepsDB()
  52276. {
  52277. if (!is_file($this->_depdb)) {
  52278. $this->rebuildDB();
  52279. return;
  52280. }
  52281. $depdb = $this->_getDepDB();
  52282. // Datatype format has been changed, rebuild the Deps DB
  52283. if ($depdb['_version'] < $this->_version) {
  52284. $this->rebuildDB();
  52285. }
  52286. if ($depdb['_version'][0] > $this->_version[0]) {
  52287. return PEAR::raiseError('Dependency database is version ' .
  52288. $depdb['_version'] . ', and we are version ' .
  52289. $this->_version . ', cannot continue');
  52290. }
  52291. }
  52292. /**
  52293. * Get a list of installed packages that depend on this package
  52294. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
  52295. * @return array|false
  52296. */
  52297. function getDependentPackages(&$pkg)
  52298. {
  52299. $data = $this->_getDepDB();
  52300. if (is_object($pkg)) {
  52301. $channel = strtolower($pkg->getChannel());
  52302. $package = strtolower($pkg->getPackage());
  52303. } else {
  52304. $channel = strtolower($pkg['channel']);
  52305. $package = strtolower($pkg['package']);
  52306. }
  52307. if (isset($data['packages'][$channel][$package])) {
  52308. return $data['packages'][$channel][$package];
  52309. }
  52310. return false;
  52311. }
  52312. /**
  52313. * Get a list of the actual dependencies of installed packages that depend on
  52314. * a package.
  52315. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
  52316. * @return array|false
  52317. */
  52318. function getDependentPackageDependencies(&$pkg)
  52319. {
  52320. $data = $this->_getDepDB();
  52321. if (is_object($pkg)) {
  52322. $channel = strtolower($pkg->getChannel());
  52323. $package = strtolower($pkg->getPackage());
  52324. } else if (is_array($pkg)) {
  52325. $channel = strtolower($pkg['channel']);
  52326. $package = strtolower($pkg['package']);
  52327. } else {
  52328. return false;
  52329. }
  52330. $depend = $this->getDependentPackages($pkg);
  52331. if (!$depend) {
  52332. return false;
  52333. }
  52334. $dependencies = array();
  52335. foreach ($depend as $info) {
  52336. $temp = $this->getDependencies($info);
  52337. foreach ($temp as $dep) {
  52338. if (
  52339. isset($dep['dep'], $dep['dep']['channel'], $dep['dep']['name']) &&
  52340. strtolower($dep['dep']['channel']) == $channel &&
  52341. strtolower($dep['dep']['name']) == $package
  52342. ) {
  52343. if (!isset($dependencies[$info['channel']])) {
  52344. $dependencies[$info['channel']] = array();
  52345. }
  52346. if (!isset($dependencies[$info['channel']][$info['package']])) {
  52347. $dependencies[$info['channel']][$info['package']] = array();
  52348. }
  52349. $dependencies[$info['channel']][$info['package']][] = $dep;
  52350. }
  52351. }
  52352. }
  52353. return $dependencies;
  52354. }
  52355. /**
  52356. * Get a list of dependencies of this installed package
  52357. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
  52358. * @return array|false
  52359. */
  52360. function getDependencies(&$pkg)
  52361. {
  52362. if (is_object($pkg)) {
  52363. $channel = strtolower($pkg->getChannel());
  52364. $package = strtolower($pkg->getPackage());
  52365. } else {
  52366. $channel = strtolower($pkg['channel']);
  52367. $package = strtolower($pkg['package']);
  52368. }
  52369. $data = $this->_getDepDB();
  52370. if (isset($data['dependencies'][$channel][$package])) {
  52371. return $data['dependencies'][$channel][$package];
  52372. }
  52373. return false;
  52374. }
  52375. /**
  52376. * Determine whether $parent depends on $child, near or deep
  52377. * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
  52378. * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
  52379. */
  52380. function dependsOn($parent, $child)
  52381. {
  52382. $c = array();
  52383. $this->_getDepDB();
  52384. return $this->_dependsOn($parent, $child, $c);
  52385. }
  52386. function _dependsOn($parent, $child, &$checked)
  52387. {
  52388. if (is_object($parent)) {
  52389. $channel = strtolower($parent->getChannel());
  52390. $package = strtolower($parent->getPackage());
  52391. } else {
  52392. $channel = strtolower($parent['channel']);
  52393. $package = strtolower($parent['package']);
  52394. }
  52395. if (is_object($child)) {
  52396. $depchannel = strtolower($child->getChannel());
  52397. $deppackage = strtolower($child->getPackage());
  52398. } else {
  52399. $depchannel = strtolower($child['channel']);
  52400. $deppackage = strtolower($child['package']);
  52401. }
  52402. if (isset($checked[$channel][$package][$depchannel][$deppackage])) {
  52403. return false; // avoid endless recursion
  52404. }
  52405. $checked[$channel][$package][$depchannel][$deppackage] = true;
  52406. if (!isset($this->_cache['dependencies'][$channel][$package])) {
  52407. return false;
  52408. }
  52409. foreach ($this->_cache['dependencies'][$channel][$package] as $info) {
  52410. if (isset($info['dep']['uri'])) {
  52411. if (is_object($child)) {
  52412. if ($info['dep']['uri'] == $child->getURI()) {
  52413. return true;
  52414. }
  52415. } elseif (isset($child['uri'])) {
  52416. if ($info['dep']['uri'] == $child['uri']) {
  52417. return true;
  52418. }
  52419. }
  52420. return false;
  52421. }
  52422. if (strtolower($info['dep']['channel']) == $depchannel &&
  52423. strtolower($info['dep']['name']) == $deppackage) {
  52424. return true;
  52425. }
  52426. }
  52427. foreach ($this->_cache['dependencies'][$channel][$package] as $info) {
  52428. if (isset($info['dep']['uri'])) {
  52429. if ($this->_dependsOn(array(
  52430. 'uri' => $info['dep']['uri'],
  52431. 'package' => $info['dep']['name']), $child, $checked)) {
  52432. return true;
  52433. }
  52434. } else {
  52435. if ($this->_dependsOn(array(
  52436. 'channel' => $info['dep']['channel'],
  52437. 'package' => $info['dep']['name']), $child, $checked)) {
  52438. return true;
  52439. }
  52440. }
  52441. }
  52442. return false;
  52443. }
  52444. /**
  52445. * Register dependencies of a package that is being installed or upgraded
  52446. * @param PEAR_PackageFile_v2|PEAR_PackageFile_v2
  52447. */
  52448. function installPackage(&$package)
  52449. {
  52450. $data = $this->_getDepDB();
  52451. unset($this->_cache);
  52452. $this->_setPackageDeps($data, $package);
  52453. $this->_writeDepDB($data);
  52454. }
  52455. /**
  52456. * Remove dependencies of a package that is being uninstalled, or upgraded.
  52457. *
  52458. * Upgraded packages first uninstall, then install
  52459. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array If an array, then it must have
  52460. * indices 'channel' and 'package'
  52461. */
  52462. function uninstallPackage(&$pkg)
  52463. {
  52464. $data = $this->_getDepDB();
  52465. unset($this->_cache);
  52466. if (is_object($pkg)) {
  52467. $channel = strtolower($pkg->getChannel());
  52468. $package = strtolower($pkg->getPackage());
  52469. } else {
  52470. $channel = strtolower($pkg['channel']);
  52471. $package = strtolower($pkg['package']);
  52472. }
  52473. if (!isset($data['dependencies'][$channel][$package])) {
  52474. return true;
  52475. }
  52476. foreach ($data['dependencies'][$channel][$package] as $dep) {
  52477. $found = false;
  52478. $depchannel = isset($dep['dep']['uri']) ? '__uri' : strtolower($dep['dep']['channel']);
  52479. $depname = strtolower($dep['dep']['name']);
  52480. if (isset($data['packages'][$depchannel][$depname])) {
  52481. foreach ($data['packages'][$depchannel][$depname] as $i => $info) {
  52482. if ($info['channel'] == $channel && $info['package'] == $package) {
  52483. $found = true;
  52484. break;
  52485. }
  52486. }
  52487. }
  52488. if ($found) {
  52489. unset($data['packages'][$depchannel][$depname][$i]);
  52490. if (!count($data['packages'][$depchannel][$depname])) {
  52491. unset($data['packages'][$depchannel][$depname]);
  52492. if (!count($data['packages'][$depchannel])) {
  52493. unset($data['packages'][$depchannel]);
  52494. }
  52495. } else {
  52496. $data['packages'][$depchannel][$depname] =
  52497. array_values($data['packages'][$depchannel][$depname]);
  52498. }
  52499. }
  52500. }
  52501. unset($data['dependencies'][$channel][$package]);
  52502. if (!count($data['dependencies'][$channel])) {
  52503. unset($data['dependencies'][$channel]);
  52504. }
  52505. if (!count($data['dependencies'])) {
  52506. unset($data['dependencies']);
  52507. }
  52508. if (!count($data['packages'])) {
  52509. unset($data['packages']);
  52510. }
  52511. $this->_writeDepDB($data);
  52512. }
  52513. /**
  52514. * Rebuild the dependency DB by reading registry entries.
  52515. * @return true|PEAR_Error
  52516. */
  52517. function rebuildDB()
  52518. {
  52519. $depdb = array('_version' => $this->_version);
  52520. if (!$this->hasWriteAccess()) {
  52521. // allow startup for read-only with older Registry
  52522. return $depdb;
  52523. }
  52524. $packages = $this->_registry->listAllPackages();
  52525. if (PEAR::isError($packages)) {
  52526. return $packages;
  52527. }
  52528. foreach ($packages as $channel => $ps) {
  52529. foreach ($ps as $package) {
  52530. $package = $this->_registry->getPackage($package, $channel);
  52531. if (PEAR::isError($package)) {
  52532. return $package;
  52533. }
  52534. $this->_setPackageDeps($depdb, $package);
  52535. }
  52536. }
  52537. $error = $this->_writeDepDB($depdb);
  52538. if (PEAR::isError($error)) {
  52539. return $error;
  52540. }
  52541. $this->_cache = $depdb;
  52542. return true;
  52543. }
  52544. /**
  52545. * Register usage of the dependency DB to prevent race conditions
  52546. * @param int one of the LOCK_* constants
  52547. * @return true|PEAR_Error
  52548. * @access private
  52549. */
  52550. function _lock($mode = LOCK_EX)
  52551. {
  52552. if (stristr(php_uname(), 'Windows 9')) {
  52553. return true;
  52554. }
  52555. if ($mode != LOCK_UN && is_resource($this->_lockFp)) {
  52556. // XXX does not check type of lock (LOCK_SH/LOCK_EX)
  52557. return true;
  52558. }
  52559. $open_mode = 'w';
  52560. // XXX People reported problems with LOCK_SH and 'w'
  52561. if ($mode === LOCK_SH) {
  52562. if (!file_exists($this->_lockfile)) {
  52563. touch($this->_lockfile);
  52564. } elseif (!is_file($this->_lockfile)) {
  52565. return PEAR::raiseError('could not create Dependency lock file, ' .
  52566. 'it exists and is not a regular file');
  52567. }
  52568. $open_mode = 'r';
  52569. }
  52570. if (!is_resource($this->_lockFp)) {
  52571. $this->_lockFp = @fopen($this->_lockfile, $open_mode);
  52572. }
  52573. if (!is_resource($this->_lockFp)) {
  52574. $last_errormsg = '';
  52575. $last_error = error_get_last();
  52576. if (!empty($last_error['message'])) {
  52577. $last_errormsg = $last_error['message'];
  52578. }
  52579. return PEAR::raiseError("could not create Dependency lock file" .
  52580. (isset($last_errormsg) ? ": " . $last_errormsg : ""));
  52581. }
  52582. if (!(int)flock($this->_lockFp, $mode)) {
  52583. switch ($mode) {
  52584. case LOCK_SH: $str = 'shared'; break;
  52585. case LOCK_EX: $str = 'exclusive'; break;
  52586. case LOCK_UN: $str = 'unlock'; break;
  52587. default: $str = 'unknown'; break;
  52588. }
  52589. return PEAR::raiseError("could not acquire $str lock ($this->_lockfile)");
  52590. }
  52591. return true;
  52592. }
  52593. /**
  52594. * Release usage of dependency DB
  52595. * @return true|PEAR_Error
  52596. * @access private
  52597. */
  52598. function _unlock()
  52599. {
  52600. $ret = $this->_lock(LOCK_UN);
  52601. if (is_resource($this->_lockFp)) {
  52602. fclose($this->_lockFp);
  52603. }
  52604. $this->_lockFp = null;
  52605. return $ret;
  52606. }
  52607. /**
  52608. * Load the dependency database from disk, or return the cache
  52609. * @return array|PEAR_Error
  52610. */
  52611. function _getDepDB()
  52612. {
  52613. if (!$this->hasWriteAccess()) {
  52614. return array('_version' => $this->_version);
  52615. }
  52616. if (isset($this->_cache)) {
  52617. return $this->_cache;
  52618. }
  52619. if (!$fp = fopen($this->_depdb, 'r')) {
  52620. $err = PEAR::raiseError("Could not open dependencies file `".$this->_depdb."'");
  52621. return $err;
  52622. }
  52623. clearstatcache();
  52624. fclose($fp);
  52625. $data = @unserialize(file_get_contents($this->_depdb));
  52626. $this->_cache = $data;
  52627. return $data;
  52628. }
  52629. /**
  52630. * Write out the dependency database to disk
  52631. * @param array the database
  52632. * @return true|PEAR_Error
  52633. * @access private
  52634. */
  52635. function _writeDepDB(&$deps)
  52636. {
  52637. if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  52638. return $e;
  52639. }
  52640. if (!$fp = fopen($this->_depdb, 'wb')) {
  52641. $this->_unlock();
  52642. return PEAR::raiseError("Could not open dependencies file `".$this->_depdb."' for writing");
  52643. }
  52644. fwrite($fp, serialize($deps));
  52645. fclose($fp);
  52646. $this->_unlock();
  52647. $this->_cache = $deps;
  52648. return true;
  52649. }
  52650. /**
  52651. * Register all dependencies from a package in the dependencies database, in essence
  52652. * "installing" the package's dependency information
  52653. * @param array the database
  52654. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  52655. * @access private
  52656. */
  52657. function _setPackageDeps(&$data, &$pkg)
  52658. {
  52659. $pkg->setConfig($this->_config);
  52660. if ($pkg->getPackagexmlVersion() == '1.0') {
  52661. $gen = &$pkg->getDefaultGenerator();
  52662. $deps = $gen->dependenciesToV2();
  52663. } else {
  52664. $deps = $pkg->getDeps(true);
  52665. }
  52666. if (!$deps) {
  52667. return;
  52668. }
  52669. if (!is_array($data)) {
  52670. $data = array();
  52671. }
  52672. if (!isset($data['dependencies'])) {
  52673. $data['dependencies'] = array();
  52674. }
  52675. $channel = strtolower($pkg->getChannel());
  52676. $package = strtolower($pkg->getPackage());
  52677. if (!isset($data['dependencies'][$channel])) {
  52678. $data['dependencies'][$channel] = array();
  52679. }
  52680. $data['dependencies'][$channel][$package] = array();
  52681. if (isset($deps['required']['package'])) {
  52682. if (!isset($deps['required']['package'][0])) {
  52683. $deps['required']['package'] = array($deps['required']['package']);
  52684. }
  52685. foreach ($deps['required']['package'] as $dep) {
  52686. $this->_registerDep($data, $pkg, $dep, 'required');
  52687. }
  52688. }
  52689. if (isset($deps['optional']['package'])) {
  52690. if (!isset($deps['optional']['package'][0])) {
  52691. $deps['optional']['package'] = array($deps['optional']['package']);
  52692. }
  52693. foreach ($deps['optional']['package'] as $dep) {
  52694. $this->_registerDep($data, $pkg, $dep, 'optional');
  52695. }
  52696. }
  52697. if (isset($deps['required']['subpackage'])) {
  52698. if (!isset($deps['required']['subpackage'][0])) {
  52699. $deps['required']['subpackage'] = array($deps['required']['subpackage']);
  52700. }
  52701. foreach ($deps['required']['subpackage'] as $dep) {
  52702. $this->_registerDep($data, $pkg, $dep, 'required');
  52703. }
  52704. }
  52705. if (isset($deps['optional']['subpackage'])) {
  52706. if (!isset($deps['optional']['subpackage'][0])) {
  52707. $deps['optional']['subpackage'] = array($deps['optional']['subpackage']);
  52708. }
  52709. foreach ($deps['optional']['subpackage'] as $dep) {
  52710. $this->_registerDep($data, $pkg, $dep, 'optional');
  52711. }
  52712. }
  52713. if (isset($deps['group'])) {
  52714. if (!isset($deps['group'][0])) {
  52715. $deps['group'] = array($deps['group']);
  52716. }
  52717. foreach ($deps['group'] as $group) {
  52718. if (isset($group['package'])) {
  52719. if (!isset($group['package'][0])) {
  52720. $group['package'] = array($group['package']);
  52721. }
  52722. foreach ($group['package'] as $dep) {
  52723. $this->_registerDep($data, $pkg, $dep, 'optional',
  52724. $group['attribs']['name']);
  52725. }
  52726. }
  52727. if (isset($group['subpackage'])) {
  52728. if (!isset($group['subpackage'][0])) {
  52729. $group['subpackage'] = array($group['subpackage']);
  52730. }
  52731. foreach ($group['subpackage'] as $dep) {
  52732. $this->_registerDep($data, $pkg, $dep, 'optional',
  52733. $group['attribs']['name']);
  52734. }
  52735. }
  52736. }
  52737. }
  52738. if ($data['dependencies'][$channel][$package] == array()) {
  52739. unset($data['dependencies'][$channel][$package]);
  52740. if (!count($data['dependencies'][$channel])) {
  52741. unset($data['dependencies'][$channel]);
  52742. }
  52743. }
  52744. }
  52745. /**
  52746. * @param array the database
  52747. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  52748. * @param array the specific dependency
  52749. * @param required|optional whether this is a required or an optional dep
  52750. * @param string|false dependency group this dependency is from, or false for ordinary dep
  52751. */
  52752. function _registerDep(&$data, &$pkg, $dep, $type, $group = false)
  52753. {
  52754. $info = array(
  52755. 'dep' => $dep,
  52756. 'type' => $type,
  52757. 'group' => $group
  52758. );
  52759. $dep = array_map('strtolower', $dep);
  52760. $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
  52761. if (!isset($data['dependencies'])) {
  52762. $data['dependencies'] = array();
  52763. }
  52764. $channel = strtolower($pkg->getChannel());
  52765. $package = strtolower($pkg->getPackage());
  52766. if (!isset($data['dependencies'][$channel])) {
  52767. $data['dependencies'][$channel] = array();
  52768. }
  52769. if (!isset($data['dependencies'][$channel][$package])) {
  52770. $data['dependencies'][$channel][$package] = array();
  52771. }
  52772. $data['dependencies'][$channel][$package][] = $info;
  52773. if (isset($data['packages'][$depchannel][$dep['name']])) {
  52774. $found = false;
  52775. foreach ($data['packages'][$depchannel][$dep['name']] as $i => $p) {
  52776. if ($p['channel'] == $channel && $p['package'] == $package) {
  52777. $found = true;
  52778. break;
  52779. }
  52780. }
  52781. } else {
  52782. if (!isset($data['packages'])) {
  52783. $data['packages'] = array();
  52784. }
  52785. if (!isset($data['packages'][$depchannel])) {
  52786. $data['packages'][$depchannel] = array();
  52787. }
  52788. if (!isset($data['packages'][$depchannel][$dep['name']])) {
  52789. $data['packages'][$depchannel][$dep['name']] = array();
  52790. }
  52791. $found = false;
  52792. }
  52793. if (!$found) {
  52794. $data['packages'][$depchannel][$dep['name']][] = array(
  52795. 'channel' => $channel,
  52796. 'package' => $package
  52797. );
  52798. }
  52799. }
  52800. }
  52801. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Dependency2.php�������������������������������������������������������������������0000664�0001750�0001750�00000142416�14720722517�015700� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  52802. /**
  52803. * PEAR_Dependency2, advanced dependency validation
  52804. *
  52805. * PHP versions 4 and 5
  52806. *
  52807. * @category pear
  52808. * @package PEAR
  52809. * @author Greg Beaver <cellog@php.net>
  52810. * @copyright 1997-2009 The Authors
  52811. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  52812. * @link http://pear.php.net/package/PEAR
  52813. * @since File available since Release 1.4.0a1
  52814. */
  52815. /**
  52816. * Required for the PEAR_VALIDATE_* constants
  52817. */
  52818. require_once 'PEAR/Validate.php';
  52819. /**
  52820. * Dependency check for PEAR packages
  52821. *
  52822. * This class handles both version 1.0 and 2.0 dependencies
  52823. * WARNING: *any* changes to this class must be duplicated in the
  52824. * test_PEAR_Dependency2 class found in tests/PEAR_Dependency2/setup.php.inc,
  52825. * or unit tests will not actually validate the changes
  52826. * @category pear
  52827. * @package PEAR
  52828. * @author Greg Beaver <cellog@php.net>
  52829. * @copyright 1997-2009 The Authors
  52830. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  52831. * @version Release: 1.10.16
  52832. * @link http://pear.php.net/package/PEAR
  52833. * @since Class available since Release 1.4.0a1
  52834. */
  52835. class PEAR_Dependency2
  52836. {
  52837. /**
  52838. * One of the PEAR_VALIDATE_* states
  52839. * @see PEAR_VALIDATE_NORMAL
  52840. * @var integer
  52841. */
  52842. var $_state;
  52843. /**
  52844. * Command-line options to install/upgrade/uninstall commands
  52845. * @param array
  52846. */
  52847. var $_options;
  52848. /**
  52849. * @var OS_Guess
  52850. */
  52851. var $_os;
  52852. /**
  52853. * @var PEAR_Registry
  52854. */
  52855. var $_registry;
  52856. /**
  52857. * @var PEAR_Config
  52858. */
  52859. var $_config;
  52860. /**
  52861. * @var PEAR_DependencyDB
  52862. */
  52863. var $_dependencydb;
  52864. /**
  52865. * Output of PEAR_Registry::parsedPackageName()
  52866. * @var array
  52867. */
  52868. var $_currentPackage;
  52869. /**
  52870. * @param PEAR_Config
  52871. * @param array installation options
  52872. * @param array format of PEAR_Registry::parsedPackageName()
  52873. * @param int installation state (one of PEAR_VALIDATE_*)
  52874. */
  52875. function __construct(&$config, $installoptions, $package,
  52876. $state = PEAR_VALIDATE_INSTALLING)
  52877. {
  52878. $this->_config = &$config;
  52879. if (!class_exists('PEAR_DependencyDB')) {
  52880. require_once 'PEAR/DependencyDB.php';
  52881. }
  52882. if (isset($installoptions['packagingroot'])) {
  52883. // make sure depdb is in the right location
  52884. $config->setInstallRoot($installoptions['packagingroot']);
  52885. }
  52886. $this->_registry = &$config->getRegistry();
  52887. $this->_dependencydb = &PEAR_DependencyDB::singleton($config);
  52888. if (isset($installoptions['packagingroot'])) {
  52889. $config->setInstallRoot(false);
  52890. }
  52891. $this->_options = $installoptions;
  52892. $this->_state = $state;
  52893. if (!class_exists('OS_Guess')) {
  52894. require_once 'OS/Guess.php';
  52895. }
  52896. $this->_os = new OS_Guess;
  52897. $this->_currentPackage = $package;
  52898. }
  52899. static function _getExtraString($dep)
  52900. {
  52901. $extra = ' (';
  52902. if (isset($dep['uri'])) {
  52903. return '';
  52904. }
  52905. if (isset($dep['recommended'])) {
  52906. $extra .= 'recommended version ' . $dep['recommended'];
  52907. } else {
  52908. if (isset($dep['min'])) {
  52909. $extra .= 'version >= ' . $dep['min'];
  52910. }
  52911. if (isset($dep['max'])) {
  52912. if ($extra != ' (') {
  52913. $extra .= ', ';
  52914. }
  52915. $extra .= 'version <= ' . $dep['max'];
  52916. }
  52917. if (isset($dep['exclude'])) {
  52918. if (!is_array($dep['exclude'])) {
  52919. $dep['exclude'] = array($dep['exclude']);
  52920. }
  52921. if ($extra != ' (') {
  52922. $extra .= ', ';
  52923. }
  52924. $extra .= 'excluded versions: ';
  52925. foreach ($dep['exclude'] as $i => $exclude) {
  52926. if ($i) {
  52927. $extra .= ', ';
  52928. }
  52929. $extra .= $exclude;
  52930. }
  52931. }
  52932. }
  52933. $extra .= ')';
  52934. if ($extra == ' ()') {
  52935. $extra = '';
  52936. }
  52937. return $extra;
  52938. }
  52939. /**
  52940. * This makes unit-testing a heck of a lot easier
  52941. */
  52942. function getPHP_OS()
  52943. {
  52944. return PHP_OS;
  52945. }
  52946. /**
  52947. * This makes unit-testing a heck of a lot easier
  52948. */
  52949. function getsysname()
  52950. {
  52951. return $this->_os->getSysname();
  52952. }
  52953. /**
  52954. * Specify a dependency on an OS. Use arch for detailed os/processor information
  52955. *
  52956. * There are two generic OS dependencies that will be the most common, unix and windows.
  52957. * Other options are linux, freebsd, darwin (OS X), sunos, irix, hpux, aix
  52958. */
  52959. function validateOsDependency($dep)
  52960. {
  52961. if ($this->_state != PEAR_VALIDATE_INSTALLING && $this->_state != PEAR_VALIDATE_DOWNLOADING) {
  52962. return true;
  52963. }
  52964. if ($dep['name'] == '*') {
  52965. return true;
  52966. }
  52967. $not = isset($dep['conflicts']) ? true : false;
  52968. switch (strtolower($dep['name'])) {
  52969. case 'windows' :
  52970. if ($not) {
  52971. if (strtolower(substr($this->getPHP_OS(), 0, 3)) == 'win') {
  52972. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  52973. return $this->raiseError("Cannot install %s on Windows");
  52974. }
  52975. return $this->warning("warning: Cannot install %s on Windows");
  52976. }
  52977. } else {
  52978. if (strtolower(substr($this->getPHP_OS(), 0, 3)) != 'win') {
  52979. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  52980. return $this->raiseError("Can only install %s on Windows");
  52981. }
  52982. return $this->warning("warning: Can only install %s on Windows");
  52983. }
  52984. }
  52985. break;
  52986. case 'unix' :
  52987. $unices = array('linux', 'freebsd', 'darwin', 'sunos', 'irix', 'hpux', 'aix');
  52988. if ($not) {
  52989. if (in_array($this->getSysname(), $unices)) {
  52990. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  52991. return $this->raiseError("Cannot install %s on any Unix system");
  52992. }
  52993. return $this->warning( "warning: Cannot install %s on any Unix system");
  52994. }
  52995. } else {
  52996. if (!in_array($this->getSysname(), $unices)) {
  52997. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  52998. return $this->raiseError("Can only install %s on a Unix system");
  52999. }
  53000. return $this->warning("warning: Can only install %s on a Unix system");
  53001. }
  53002. }
  53003. break;
  53004. default :
  53005. if ($not) {
  53006. if (strtolower($dep['name']) == strtolower($this->getSysname())) {
  53007. if (!isset($this->_options['nodeps']) &&
  53008. !isset($this->_options['force'])) {
  53009. return $this->raiseError('Cannot install %s on ' . $dep['name'] .
  53010. ' operating system');
  53011. }
  53012. return $this->warning('warning: Cannot install %s on ' .
  53013. $dep['name'] . ' operating system');
  53014. }
  53015. } else {
  53016. if (strtolower($dep['name']) != strtolower($this->getSysname())) {
  53017. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53018. return $this->raiseError('Cannot install %s on ' .
  53019. $this->getSysname() .
  53020. ' operating system, can only install on ' . $dep['name']);
  53021. }
  53022. return $this->warning('warning: Cannot install %s on ' .
  53023. $this->getSysname() .
  53024. ' operating system, can only install on ' . $dep['name']);
  53025. }
  53026. }
  53027. }
  53028. return true;
  53029. }
  53030. /**
  53031. * This makes unit-testing a heck of a lot easier
  53032. */
  53033. function matchSignature($pattern)
  53034. {
  53035. return $this->_os->matchSignature($pattern);
  53036. }
  53037. /**
  53038. * Specify a complex dependency on an OS/processor/kernel version,
  53039. * Use OS for simple operating system dependency.
  53040. *
  53041. * This is the only dependency that accepts an eregable pattern. The pattern
  53042. * will be matched against the php_uname() output parsed by OS_Guess
  53043. */
  53044. function validateArchDependency($dep)
  53045. {
  53046. if ($this->_state != PEAR_VALIDATE_INSTALLING) {
  53047. return true;
  53048. }
  53049. $not = isset($dep['conflicts']) ? true : false;
  53050. if (!$this->matchSignature($dep['pattern'])) {
  53051. if (!$not) {
  53052. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53053. return $this->raiseError('%s Architecture dependency failed, does not ' .
  53054. 'match "' . $dep['pattern'] . '"');
  53055. }
  53056. return $this->warning('warning: %s Architecture dependency failed, does ' .
  53057. 'not match "' . $dep['pattern'] . '"');
  53058. }
  53059. return true;
  53060. }
  53061. if ($not) {
  53062. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53063. return $this->raiseError('%s Architecture dependency failed, required "' .
  53064. $dep['pattern'] . '"');
  53065. }
  53066. return $this->warning('warning: %s Architecture dependency failed, ' .
  53067. 'required "' . $dep['pattern'] . '"');
  53068. }
  53069. return true;
  53070. }
  53071. /**
  53072. * This makes unit-testing a heck of a lot easier
  53073. */
  53074. function extension_loaded($name)
  53075. {
  53076. return extension_loaded($name);
  53077. }
  53078. /**
  53079. * This makes unit-testing a heck of a lot easier
  53080. */
  53081. function phpversion($name = null)
  53082. {
  53083. if ($name !== null) {
  53084. return phpversion($name);
  53085. }
  53086. return phpversion();
  53087. }
  53088. function validateExtensionDependency($dep, $required = true)
  53089. {
  53090. if ($this->_state != PEAR_VALIDATE_INSTALLING &&
  53091. $this->_state != PEAR_VALIDATE_DOWNLOADING) {
  53092. return true;
  53093. }
  53094. $loaded = $this->extension_loaded($dep['name']);
  53095. $extra = self::_getExtraString($dep);
  53096. if (isset($dep['exclude'])) {
  53097. if (!is_array($dep['exclude'])) {
  53098. $dep['exclude'] = array($dep['exclude']);
  53099. }
  53100. }
  53101. if (!isset($dep['min']) && !isset($dep['max']) &&
  53102. !isset($dep['recommended']) && !isset($dep['exclude'])
  53103. ) {
  53104. if ($loaded) {
  53105. if (isset($dep['conflicts'])) {
  53106. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53107. return $this->raiseError('%s conflicts with PHP extension "' .
  53108. $dep['name'] . '"' . $extra);
  53109. }
  53110. return $this->warning('warning: %s conflicts with PHP extension "' .
  53111. $dep['name'] . '"' . $extra);
  53112. }
  53113. return true;
  53114. }
  53115. if (isset($dep['conflicts'])) {
  53116. return true;
  53117. }
  53118. if ($required) {
  53119. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53120. return $this->raiseError('%s requires PHP extension "' .
  53121. $dep['name'] . '"' . $extra);
  53122. }
  53123. return $this->warning('warning: %s requires PHP extension "' .
  53124. $dep['name'] . '"' . $extra);
  53125. }
  53126. return $this->warning('%s can optionally use PHP extension "' .
  53127. $dep['name'] . '"' . $extra);
  53128. }
  53129. if (!$loaded) {
  53130. if (isset($dep['conflicts'])) {
  53131. return true;
  53132. }
  53133. if (!$required) {
  53134. return $this->warning('%s can optionally use PHP extension "' .
  53135. $dep['name'] . '"' . $extra);
  53136. }
  53137. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53138. return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
  53139. '"' . $extra);
  53140. }
  53141. return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
  53142. '"' . $extra);
  53143. }
  53144. $version = (string) $this->phpversion($dep['name']);
  53145. if (empty($version)) {
  53146. $version = '0';
  53147. }
  53148. $fail = false;
  53149. if (isset($dep['min']) && !version_compare($version, $dep['min'], '>=')) {
  53150. $fail = true;
  53151. }
  53152. if (isset($dep['max']) && !version_compare($version, $dep['max'], '<=')) {
  53153. $fail = true;
  53154. }
  53155. if ($fail && !isset($dep['conflicts'])) {
  53156. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53157. return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
  53158. '"' . $extra . ', installed version is ' . $version);
  53159. }
  53160. return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
  53161. '"' . $extra . ', installed version is ' . $version);
  53162. } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail && isset($dep['conflicts'])) {
  53163. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53164. return $this->raiseError('%s conflicts with PHP extension "' .
  53165. $dep['name'] . '"' . $extra . ', installed version is ' . $version);
  53166. }
  53167. return $this->warning('warning: %s conflicts with PHP extension "' .
  53168. $dep['name'] . '"' . $extra . ', installed version is ' . $version);
  53169. }
  53170. if (isset($dep['exclude'])) {
  53171. foreach ($dep['exclude'] as $exclude) {
  53172. if (version_compare($version, $exclude, '==')) {
  53173. if (isset($dep['conflicts'])) {
  53174. continue;
  53175. }
  53176. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53177. return $this->raiseError('%s is not compatible with PHP extension "' .
  53178. $dep['name'] . '" version ' .
  53179. $exclude);
  53180. }
  53181. return $this->warning('warning: %s is not compatible with PHP extension "' .
  53182. $dep['name'] . '" version ' .
  53183. $exclude);
  53184. } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
  53185. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53186. return $this->raiseError('%s conflicts with PHP extension "' .
  53187. $dep['name'] . '"' . $extra . ', installed version is ' . $version);
  53188. }
  53189. return $this->warning('warning: %s conflicts with PHP extension "' .
  53190. $dep['name'] . '"' . $extra . ', installed version is ' . $version);
  53191. }
  53192. }
  53193. }
  53194. if (isset($dep['recommended'])) {
  53195. if (version_compare($version, $dep['recommended'], '==')) {
  53196. return true;
  53197. }
  53198. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53199. return $this->raiseError('%s dependency: PHP extension ' . $dep['name'] .
  53200. ' version "' . $version . '"' .
  53201. ' is not the recommended version "' . $dep['recommended'] .
  53202. '", but may be compatible, use --force to install');
  53203. }
  53204. return $this->warning('warning: %s dependency: PHP extension ' .
  53205. $dep['name'] . ' version "' . $version . '"' .
  53206. ' is not the recommended version "' . $dep['recommended'].'"');
  53207. }
  53208. return true;
  53209. }
  53210. function validatePhpDependency($dep)
  53211. {
  53212. if ($this->_state != PEAR_VALIDATE_INSTALLING &&
  53213. $this->_state != PEAR_VALIDATE_DOWNLOADING) {
  53214. return true;
  53215. }
  53216. $version = $this->phpversion();
  53217. $extra = self::_getExtraString($dep);
  53218. if (isset($dep['exclude'])) {
  53219. if (!is_array($dep['exclude'])) {
  53220. $dep['exclude'] = array($dep['exclude']);
  53221. }
  53222. }
  53223. if (isset($dep['min'])) {
  53224. if (!version_compare($version, $dep['min'], '>=')) {
  53225. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53226. return $this->raiseError('%s requires PHP' .
  53227. $extra . ', installed version is ' . $version);
  53228. }
  53229. return $this->warning('warning: %s requires PHP' .
  53230. $extra . ', installed version is ' . $version);
  53231. }
  53232. }
  53233. if (isset($dep['max'])) {
  53234. if (!version_compare($version, $dep['max'], '<=')) {
  53235. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53236. return $this->raiseError('%s requires PHP' .
  53237. $extra . ', installed version is ' . $version);
  53238. }
  53239. return $this->warning('warning: %s requires PHP' .
  53240. $extra . ', installed version is ' . $version);
  53241. }
  53242. }
  53243. if (isset($dep['exclude'])) {
  53244. foreach ($dep['exclude'] as $exclude) {
  53245. if (version_compare($version, $exclude, '==')) {
  53246. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53247. return $this->raiseError('%s is not compatible with PHP version ' .
  53248. $exclude);
  53249. }
  53250. return $this->warning(
  53251. 'warning: %s is not compatible with PHP version ' .
  53252. $exclude);
  53253. }
  53254. }
  53255. }
  53256. return true;
  53257. }
  53258. /**
  53259. * This makes unit-testing a heck of a lot easier
  53260. */
  53261. function getPEARVersion()
  53262. {
  53263. return '1.10.16';
  53264. }
  53265. function validatePearinstallerDependency($dep)
  53266. {
  53267. $pearversion = $this->getPEARVersion();
  53268. $extra = self::_getExtraString($dep);
  53269. if (isset($dep['exclude'])) {
  53270. if (!is_array($dep['exclude'])) {
  53271. $dep['exclude'] = array($dep['exclude']);
  53272. }
  53273. }
  53274. if (version_compare($pearversion, $dep['min'], '<')) {
  53275. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53276. return $this->raiseError('%s requires PEAR Installer' . $extra .
  53277. ', installed version is ' . $pearversion);
  53278. }
  53279. return $this->warning('warning: %s requires PEAR Installer' . $extra .
  53280. ', installed version is ' . $pearversion);
  53281. }
  53282. if (isset($dep['max'])) {
  53283. if (version_compare($pearversion, $dep['max'], '>')) {
  53284. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53285. return $this->raiseError('%s requires PEAR Installer' . $extra .
  53286. ', installed version is ' . $pearversion);
  53287. }
  53288. return $this->warning('warning: %s requires PEAR Installer' . $extra .
  53289. ', installed version is ' . $pearversion);
  53290. }
  53291. }
  53292. if (isset($dep['exclude'])) {
  53293. if (!isset($dep['exclude'][0])) {
  53294. $dep['exclude'] = array($dep['exclude']);
  53295. }
  53296. foreach ($dep['exclude'] as $exclude) {
  53297. if (version_compare($exclude, $pearversion, '==')) {
  53298. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53299. return $this->raiseError('%s is not compatible with PEAR Installer ' .
  53300. 'version ' . $exclude);
  53301. }
  53302. return $this->warning('warning: %s is not compatible with PEAR ' .
  53303. 'Installer version ' . $exclude);
  53304. }
  53305. }
  53306. }
  53307. return true;
  53308. }
  53309. function validateSubpackageDependency($dep, $required, $params)
  53310. {
  53311. return $this->validatePackageDependency($dep, $required, $params);
  53312. }
  53313. /**
  53314. * @param array dependency information (2.0 format)
  53315. * @param boolean whether this is a required dependency
  53316. * @param array a list of downloaded packages to be installed, if any
  53317. * @param boolean if true, then deps on pear.php.net that fail will also check
  53318. * against pecl.php.net packages to accommodate extensions that have
  53319. * moved to pecl.php.net from pear.php.net
  53320. */
  53321. function validatePackageDependency($dep, $required, $params, $depv1 = false)
  53322. {
  53323. if ($this->_state != PEAR_VALIDATE_INSTALLING &&
  53324. $this->_state != PEAR_VALIDATE_DOWNLOADING) {
  53325. return true;
  53326. }
  53327. if (isset($dep['providesextension'])) {
  53328. if ($this->extension_loaded($dep['providesextension'])) {
  53329. $save = $dep;
  53330. $subdep = $dep;
  53331. $subdep['name'] = $subdep['providesextension'];
  53332. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  53333. $ret = $this->validateExtensionDependency($subdep, $required);
  53334. PEAR::popErrorHandling();
  53335. if (!PEAR::isError($ret)) {
  53336. return true;
  53337. }
  53338. }
  53339. }
  53340. if ($this->_state == PEAR_VALIDATE_INSTALLING) {
  53341. return $this->_validatePackageInstall($dep, $required, $depv1);
  53342. }
  53343. if ($this->_state == PEAR_VALIDATE_DOWNLOADING) {
  53344. return $this->_validatePackageDownload($dep, $required, $params, $depv1);
  53345. }
  53346. }
  53347. function _validatePackageDownload($dep, $required, $params, $depv1 = false)
  53348. {
  53349. $dep['package'] = $dep['name'];
  53350. if (isset($dep['uri'])) {
  53351. $dep['channel'] = '__uri';
  53352. }
  53353. $depname = $this->_registry->parsedPackageNameToString($dep, true);
  53354. $found = false;
  53355. foreach ($params as $param) {
  53356. if ($param->isEqual(
  53357. array('package' => $dep['name'],
  53358. 'channel' => $dep['channel']))) {
  53359. $found = true;
  53360. break;
  53361. }
  53362. if ($depv1 && $dep['channel'] == 'pear.php.net') {
  53363. if ($param->isEqual(
  53364. array('package' => $dep['name'],
  53365. 'channel' => 'pecl.php.net'))) {
  53366. $found = true;
  53367. break;
  53368. }
  53369. }
  53370. }
  53371. if (!$found && isset($dep['providesextension'])) {
  53372. foreach ($params as $param) {
  53373. if ($param->isExtension($dep['providesextension'])) {
  53374. $found = true;
  53375. break;
  53376. }
  53377. }
  53378. }
  53379. if ($found) {
  53380. $version = $param->getVersion();
  53381. $installed = false;
  53382. $downloaded = true;
  53383. } else {
  53384. if ($this->_registry->packageExists($dep['name'], $dep['channel'])) {
  53385. $installed = true;
  53386. $downloaded = false;
  53387. $version = $this->_registry->packageinfo($dep['name'], 'version',
  53388. $dep['channel']);
  53389. } else {
  53390. if ($dep['channel'] == 'pecl.php.net' && $this->_registry->packageExists($dep['name'],
  53391. 'pear.php.net')) {
  53392. $installed = true;
  53393. $downloaded = false;
  53394. $version = $this->_registry->packageinfo($dep['name'], 'version',
  53395. 'pear.php.net');
  53396. } else {
  53397. $version = 'not installed or downloaded';
  53398. $installed = false;
  53399. $downloaded = false;
  53400. }
  53401. }
  53402. }
  53403. $extra = self::_getExtraString($dep);
  53404. if (isset($dep['exclude']) && !is_array($dep['exclude'])) {
  53405. $dep['exclude'] = array($dep['exclude']);
  53406. }
  53407. if (!isset($dep['min']) && !isset($dep['max']) &&
  53408. !isset($dep['recommended']) && !isset($dep['exclude'])
  53409. ) {
  53410. if ($installed || $downloaded) {
  53411. $installed = $installed ? 'installed' : 'downloaded';
  53412. if (isset($dep['conflicts'])) {
  53413. $rest = '';
  53414. if ($version) {
  53415. $rest = ", $installed version is " . $version;
  53416. }
  53417. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53418. return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra . $rest);
  53419. }
  53420. return $this->warning('warning: %s conflicts with package "' . $depname . '"' . $extra . $rest);
  53421. }
  53422. return true;
  53423. }
  53424. if (isset($dep['conflicts'])) {
  53425. return true;
  53426. }
  53427. if ($required) {
  53428. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53429. return $this->raiseError('%s requires package "' . $depname . '"' . $extra);
  53430. }
  53431. return $this->warning('warning: %s requires package "' . $depname . '"' . $extra);
  53432. }
  53433. return $this->warning('%s can optionally use package "' . $depname . '"' . $extra);
  53434. }
  53435. if (!$installed && !$downloaded) {
  53436. if (isset($dep['conflicts'])) {
  53437. return true;
  53438. }
  53439. if ($required) {
  53440. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53441. return $this->raiseError('%s requires package "' . $depname . '"' . $extra);
  53442. }
  53443. return $this->warning('warning: %s requires package "' . $depname . '"' . $extra);
  53444. }
  53445. return $this->warning('%s can optionally use package "' . $depname . '"' . $extra);
  53446. }
  53447. $fail = false;
  53448. if (isset($dep['min']) && version_compare($version, $dep['min'], '<')) {
  53449. $fail = true;
  53450. }
  53451. if (isset($dep['max']) && version_compare($version, $dep['max'], '>')) {
  53452. $fail = true;
  53453. }
  53454. if ($fail && !isset($dep['conflicts'])) {
  53455. $installed = $installed ? 'installed' : 'downloaded';
  53456. $dep['package'] = $dep['name'];
  53457. $dep = $this->_registry->parsedPackageNameToString($dep, true);
  53458. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53459. return $this->raiseError('%s requires package "' . $depname . '"' .
  53460. $extra . ", $installed version is " . $version);
  53461. }
  53462. return $this->warning('warning: %s requires package "' . $depname . '"' .
  53463. $extra . ", $installed version is " . $version);
  53464. } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail &&
  53465. isset($dep['conflicts']) && !isset($dep['exclude'])) {
  53466. $installed = $installed ? 'installed' : 'downloaded';
  53467. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53468. return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra .
  53469. ", $installed version is " . $version);
  53470. }
  53471. return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
  53472. $extra . ", $installed version is " . $version);
  53473. }
  53474. if (isset($dep['exclude'])) {
  53475. $installed = $installed ? 'installed' : 'downloaded';
  53476. foreach ($dep['exclude'] as $exclude) {
  53477. if (version_compare($version, $exclude, '==') && !isset($dep['conflicts'])) {
  53478. if (!isset($this->_options['nodeps']) &&
  53479. !isset($this->_options['force'])
  53480. ) {
  53481. return $this->raiseError('%s is not compatible with ' .
  53482. $installed . ' package "' .
  53483. $depname . '" version ' .
  53484. $exclude);
  53485. }
  53486. return $this->warning('warning: %s is not compatible with ' .
  53487. $installed . ' package "' .
  53488. $depname . '" version ' .
  53489. $exclude);
  53490. } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
  53491. $installed = $installed ? 'installed' : 'downloaded';
  53492. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53493. return $this->raiseError('%s conflicts with package "' . $depname . '"' .
  53494. $extra . ", $installed version is " . $version);
  53495. }
  53496. return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
  53497. $extra . ", $installed version is " . $version);
  53498. }
  53499. }
  53500. }
  53501. if (isset($dep['recommended'])) {
  53502. $installed = $installed ? 'installed' : 'downloaded';
  53503. if (version_compare($version, $dep['recommended'], '==')) {
  53504. return true;
  53505. }
  53506. if (!$found && $installed) {
  53507. $param = $this->_registry->getPackage($dep['name'], $dep['channel']);
  53508. }
  53509. if ($param) {
  53510. $found = false;
  53511. foreach ($params as $parent) {
  53512. if ($parent->isEqual($this->_currentPackage)) {
  53513. $found = true;
  53514. break;
  53515. }
  53516. }
  53517. if ($found) {
  53518. if ($param->isCompatible($parent)) {
  53519. return true;
  53520. }
  53521. } else { // this is for validPackage() calls
  53522. $parent = $this->_registry->getPackage($this->_currentPackage['package'],
  53523. $this->_currentPackage['channel']);
  53524. if ($parent !== null && $param->isCompatible($parent)) {
  53525. return true;
  53526. }
  53527. }
  53528. }
  53529. if (!isset($this->_options['nodeps']) && !isset($this->_options['force']) &&
  53530. !isset($this->_options['loose'])
  53531. ) {
  53532. return $this->raiseError('%s dependency package "' . $depname .
  53533. '" ' . $installed . ' version ' . $version .
  53534. ' is not the recommended version ' . $dep['recommended'] .
  53535. ', but may be compatible, use --force to install');
  53536. }
  53537. return $this->warning('warning: %s dependency package "' . $depname .
  53538. '" ' . $installed . ' version ' . $version .
  53539. ' is not the recommended version ' . $dep['recommended']);
  53540. }
  53541. return true;
  53542. }
  53543. function _validatePackageInstall($dep, $required, $depv1 = false)
  53544. {
  53545. return $this->_validatePackageDownload($dep, $required, array(), $depv1);
  53546. }
  53547. /**
  53548. * Verify that uninstalling packages passed in to command line is OK.
  53549. *
  53550. * @param PEAR_Installer $dl
  53551. * @return PEAR_Error|true
  53552. */
  53553. function validatePackageUninstall(&$dl)
  53554. {
  53555. if (PEAR::isError($this->_dependencydb)) {
  53556. return $this->_dependencydb;
  53557. }
  53558. $params = array();
  53559. // construct an array of "downloaded" packages to fool the package dependency checker
  53560. // into using these to validate uninstalls of circular dependencies
  53561. $downloaded = &$dl->getUninstallPackages();
  53562. foreach ($downloaded as $i => $pf) {
  53563. if (!class_exists('PEAR_Downloader_Package')) {
  53564. require_once 'PEAR/Downloader/Package.php';
  53565. }
  53566. $dp = new PEAR_Downloader_Package($dl);
  53567. $dp->setPackageFile($downloaded[$i]);
  53568. $params[$i] = $dp;
  53569. }
  53570. // check cache
  53571. $memyselfandI = strtolower($this->_currentPackage['channel']) . '/' .
  53572. strtolower($this->_currentPackage['package']);
  53573. if (isset($dl->___uninstall_package_cache)) {
  53574. $badpackages = $dl->___uninstall_package_cache;
  53575. if (isset($badpackages[$memyselfandI]['warnings'])) {
  53576. foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
  53577. $dl->log(0, $warning[0]);
  53578. }
  53579. }
  53580. if (isset($badpackages[$memyselfandI]['errors'])) {
  53581. foreach ($badpackages[$memyselfandI]['errors'] as $error) {
  53582. if (is_array($error)) {
  53583. $dl->log(0, $error[0]);
  53584. } else {
  53585. $dl->log(0, $error->getMessage());
  53586. }
  53587. }
  53588. if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
  53589. return $this->warning(
  53590. 'warning: %s should not be uninstalled, other installed packages depend ' .
  53591. 'on this package');
  53592. }
  53593. return $this->raiseError(
  53594. '%s cannot be uninstalled, other installed packages depend on this package');
  53595. }
  53596. return true;
  53597. }
  53598. // first, list the immediate parents of each package to be uninstalled
  53599. $perpackagelist = array();
  53600. $allparents = array();
  53601. foreach ($params as $i => $param) {
  53602. $a = array(
  53603. 'channel' => strtolower($param->getChannel()),
  53604. 'package' => strtolower($param->getPackage())
  53605. );
  53606. $deps = $this->_dependencydb->getDependentPackages($a);
  53607. if ($deps) {
  53608. foreach ($deps as $d) {
  53609. $pardeps = $this->_dependencydb->getDependencies($d);
  53610. foreach ($pardeps as $dep) {
  53611. if (strtolower($dep['dep']['channel']) == $a['channel'] &&
  53612. strtolower($dep['dep']['name']) == $a['package']) {
  53613. if (!isset($perpackagelist[$a['channel'] . '/' . $a['package']])) {
  53614. $perpackagelist[$a['channel'] . '/' . $a['package']] = array();
  53615. }
  53616. $perpackagelist[$a['channel'] . '/' . $a['package']][]
  53617. = array($d['channel'] . '/' . $d['package'], $dep);
  53618. if (!isset($allparents[$d['channel'] . '/' . $d['package']])) {
  53619. $allparents[$d['channel'] . '/' . $d['package']] = array();
  53620. }
  53621. if (!isset($allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']])) {
  53622. $allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']] = array();
  53623. }
  53624. $allparents[$d['channel'] . '/' . $d['package']]
  53625. [$a['channel'] . '/' . $a['package']][]
  53626. = array($d, $dep);
  53627. }
  53628. }
  53629. }
  53630. }
  53631. }
  53632. // next, remove any packages from the parents list that are not installed
  53633. $remove = array();
  53634. foreach ($allparents as $parent => $d1) {
  53635. foreach ($d1 as $d) {
  53636. if ($this->_registry->packageExists($d[0][0]['package'], $d[0][0]['channel'])) {
  53637. continue;
  53638. }
  53639. $remove[$parent] = true;
  53640. }
  53641. }
  53642. // next remove any packages from the parents list that are not passed in for
  53643. // uninstallation
  53644. foreach ($allparents as $parent => $d1) {
  53645. foreach ($d1 as $d) {
  53646. foreach ($params as $param) {
  53647. if (strtolower($param->getChannel()) == $d[0][0]['channel'] &&
  53648. strtolower($param->getPackage()) == $d[0][0]['package']) {
  53649. // found it
  53650. continue 3;
  53651. }
  53652. }
  53653. $remove[$parent] = true;
  53654. }
  53655. }
  53656. // remove all packages whose dependencies fail
  53657. // save which ones failed for error reporting
  53658. $badchildren = array();
  53659. do {
  53660. $fail = false;
  53661. foreach ($remove as $package => $unused) {
  53662. if (!isset($allparents[$package])) {
  53663. continue;
  53664. }
  53665. foreach ($allparents[$package] as $kid => $d1) {
  53666. foreach ($d1 as $depinfo) {
  53667. if ($depinfo[1]['type'] != 'optional') {
  53668. if (isset($badchildren[$kid])) {
  53669. continue;
  53670. }
  53671. $badchildren[$kid] = true;
  53672. $remove[$kid] = true;
  53673. $fail = true;
  53674. continue 2;
  53675. }
  53676. }
  53677. }
  53678. if ($fail) {
  53679. // start over, we removed some children
  53680. continue 2;
  53681. }
  53682. }
  53683. } while ($fail);
  53684. // next, construct the list of packages that can't be uninstalled
  53685. $badpackages = array();
  53686. $save = $this->_currentPackage;
  53687. foreach ($perpackagelist as $package => $packagedeps) {
  53688. foreach ($packagedeps as $parent) {
  53689. if (!isset($remove[$parent[0]])) {
  53690. continue;
  53691. }
  53692. $packagename = $this->_registry->parsePackageName($parent[0]);
  53693. $packagename['channel'] = $this->_registry->channelAlias($packagename['channel']);
  53694. $pa = $this->_registry->getPackage($packagename['package'], $packagename['channel']);
  53695. $packagename['package'] = $pa->getPackage();
  53696. $this->_currentPackage = $packagename;
  53697. // parent is not present in uninstall list, make sure we can actually
  53698. // uninstall it (parent dep is optional)
  53699. $parentname['channel'] = $this->_registry->channelAlias($parent[1]['dep']['channel']);
  53700. $pa = $this->_registry->getPackage($parent[1]['dep']['name'], $parent[1]['dep']['channel']);
  53701. $parentname['package'] = $pa->getPackage();
  53702. $parent[1]['dep']['package'] = $parentname['package'];
  53703. $parent[1]['dep']['channel'] = $parentname['channel'];
  53704. if ($parent[1]['type'] == 'optional') {
  53705. $test = $this->_validatePackageUninstall($parent[1]['dep'], false, $dl);
  53706. if ($test !== true) {
  53707. $badpackages[$package]['warnings'][] = $test;
  53708. }
  53709. } else {
  53710. $test = $this->_validatePackageUninstall($parent[1]['dep'], true, $dl);
  53711. if ($test !== true) {
  53712. $badpackages[$package]['errors'][] = $test;
  53713. }
  53714. }
  53715. }
  53716. }
  53717. $this->_currentPackage = $save;
  53718. $dl->___uninstall_package_cache = $badpackages;
  53719. if (isset($badpackages[$memyselfandI])) {
  53720. if (isset($badpackages[$memyselfandI]['warnings'])) {
  53721. foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
  53722. $dl->log(0, $warning[0]);
  53723. }
  53724. }
  53725. if (isset($badpackages[$memyselfandI]['errors'])) {
  53726. foreach ($badpackages[$memyselfandI]['errors'] as $error) {
  53727. if (is_array($error)) {
  53728. $dl->log(0, $error[0]);
  53729. } else {
  53730. $dl->log(0, $error->getMessage());
  53731. }
  53732. }
  53733. if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
  53734. return $this->warning(
  53735. 'warning: %s should not be uninstalled, other installed packages depend ' .
  53736. 'on this package');
  53737. }
  53738. return $this->raiseError(
  53739. '%s cannot be uninstalled, other installed packages depend on this package');
  53740. }
  53741. }
  53742. return true;
  53743. }
  53744. function _validatePackageUninstall($dep, $required, $dl)
  53745. {
  53746. $depname = $this->_registry->parsedPackageNameToString($dep, true);
  53747. $version = $this->_registry->packageinfo($dep['package'], 'version', $dep['channel']);
  53748. if (!$version) {
  53749. return true;
  53750. }
  53751. $extra = self::_getExtraString($dep);
  53752. if (isset($dep['exclude']) && !is_array($dep['exclude'])) {
  53753. $dep['exclude'] = array($dep['exclude']);
  53754. }
  53755. if (isset($dep['conflicts'])) {
  53756. return true; // uninstall OK - these packages conflict (probably installed with --force)
  53757. }
  53758. if (!isset($dep['min']) && !isset($dep['max'])) {
  53759. if (!$required) {
  53760. return $this->warning('"' . $depname . '" can be optionally used by ' .
  53761. 'installed package %s' . $extra);
  53762. }
  53763. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53764. return $this->raiseError('"' . $depname . '" is required by ' .
  53765. 'installed package %s' . $extra);
  53766. }
  53767. return $this->warning('warning: "' . $depname . '" is required by ' .
  53768. 'installed package %s' . $extra);
  53769. }
  53770. $fail = false;
  53771. if (isset($dep['min']) && version_compare($version, $dep['min'], '>=')) {
  53772. $fail = true;
  53773. }
  53774. if (isset($dep['max']) && version_compare($version, $dep['max'], '<=')) {
  53775. $fail = true;
  53776. }
  53777. // we re-use this variable, preserve the original value
  53778. $saverequired = $required;
  53779. if (!$required) {
  53780. return $this->warning($depname . $extra . ' can be optionally used by installed package' .
  53781. ' "%s"');
  53782. }
  53783. if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
  53784. return $this->raiseError($depname . $extra . ' is required by installed package' .
  53785. ' "%s"');
  53786. }
  53787. return $this->raiseError('warning: ' . $depname . $extra .
  53788. ' is required by installed package "%s"');
  53789. }
  53790. /**
  53791. * validate a downloaded package against installed packages
  53792. *
  53793. * As of PEAR 1.4.3, this will only validate
  53794. *
  53795. * @param array|PEAR_Downloader_Package|PEAR_PackageFile_v1|PEAR_PackageFile_v2
  53796. * $pkg package identifier (either
  53797. * array('package' => blah, 'channel' => blah) or an array with
  53798. * index 'info' referencing an object)
  53799. * @param PEAR_Downloader $dl
  53800. * @param array $params full list of packages to install
  53801. * @return true|PEAR_Error
  53802. */
  53803. function validatePackage($pkg, &$dl, $params = array())
  53804. {
  53805. if (is_array($pkg) && isset($pkg['info'])) {
  53806. $deps = $this->_dependencydb->getDependentPackageDependencies($pkg['info']);
  53807. } else {
  53808. $deps = $this->_dependencydb->getDependentPackageDependencies($pkg);
  53809. }
  53810. $fail = false;
  53811. if ($deps) {
  53812. if (!class_exists('PEAR_Downloader_Package')) {
  53813. require_once 'PEAR/Downloader/Package.php';
  53814. }
  53815. $dp = new PEAR_Downloader_Package($dl);
  53816. if (is_object($pkg)) {
  53817. $dp->setPackageFile($pkg);
  53818. } else {
  53819. $dp->setDownloadURL($pkg);
  53820. }
  53821. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  53822. foreach ($deps as $channel => $info) {
  53823. foreach ($info as $package => $ds) {
  53824. foreach ($params as $packd) {
  53825. if (strtolower($packd->getPackage()) == strtolower($package) &&
  53826. $packd->getChannel() == $channel) {
  53827. $dl->log(3, 'skipping installed package check of "' .
  53828. $this->_registry->parsedPackageNameToString(
  53829. array('channel' => $channel, 'package' => $package),
  53830. true) .
  53831. '", version "' . $packd->getVersion() . '" will be ' .
  53832. 'downloaded and installed');
  53833. continue 2; // jump to next package
  53834. }
  53835. }
  53836. foreach ($ds as $d) {
  53837. $checker = new PEAR_Dependency2($this->_config, $this->_options,
  53838. array('channel' => $channel, 'package' => $package), $this->_state);
  53839. $dep = $d['dep'];
  53840. $required = $d['type'] == 'required';
  53841. $ret = $checker->_validatePackageDownload($dep, $required, array(&$dp));
  53842. if (is_array($ret)) {
  53843. $dl->log(0, $ret[0]);
  53844. } elseif (PEAR::isError($ret)) {
  53845. $dl->log(0, $ret->getMessage());
  53846. $fail = true;
  53847. }
  53848. }
  53849. }
  53850. }
  53851. PEAR::popErrorHandling();
  53852. }
  53853. if ($fail) {
  53854. return $this->raiseError(
  53855. '%s cannot be installed, conflicts with installed packages');
  53856. }
  53857. return true;
  53858. }
  53859. /**
  53860. * validate a package.xml 1.0 dependency
  53861. */
  53862. function validateDependency1($dep, $params = array())
  53863. {
  53864. if (!isset($dep['optional'])) {
  53865. $dep['optional'] = 'no';
  53866. }
  53867. list($newdep, $type) = self::normalizeDep($dep);
  53868. if (!$newdep) {
  53869. return $this->raiseError("Invalid Dependency");
  53870. }
  53871. if (method_exists($this, "validate{$type}Dependency")) {
  53872. return $this->{"validate{$type}Dependency"}($newdep, $dep['optional'] == 'no',
  53873. $params, true);
  53874. }
  53875. }
  53876. /**
  53877. * Convert a 1.0 dep into a 2.0 dep
  53878. */
  53879. static function normalizeDep($dep)
  53880. {
  53881. $types = array(
  53882. 'pkg' => 'Package',
  53883. 'ext' => 'Extension',
  53884. 'os' => 'Os',
  53885. 'php' => 'Php'
  53886. );
  53887. if (!isset($types[$dep['type']])) {
  53888. return array(false, false);
  53889. }
  53890. $type = $types[$dep['type']];
  53891. $newdep = array();
  53892. switch ($type) {
  53893. case 'Package' :
  53894. $newdep['channel'] = 'pear.php.net';
  53895. case 'Extension' :
  53896. case 'Os' :
  53897. $newdep['name'] = $dep['name'];
  53898. break;
  53899. }
  53900. $dep['rel'] = PEAR_Dependency2::signOperator($dep['rel']);
  53901. switch ($dep['rel']) {
  53902. case 'has' :
  53903. return array($newdep, $type);
  53904. break;
  53905. case 'not' :
  53906. $newdep['conflicts'] = true;
  53907. break;
  53908. case '>=' :
  53909. case '>' :
  53910. $newdep['min'] = $dep['version'];
  53911. if ($dep['rel'] == '>') {
  53912. $newdep['exclude'] = $dep['version'];
  53913. }
  53914. break;
  53915. case '<=' :
  53916. case '<' :
  53917. $newdep['max'] = $dep['version'];
  53918. if ($dep['rel'] == '<') {
  53919. $newdep['exclude'] = $dep['version'];
  53920. }
  53921. break;
  53922. case 'ne' :
  53923. case '!=' :
  53924. $newdep['min'] = '0';
  53925. $newdep['max'] = '100000';
  53926. $newdep['exclude'] = $dep['version'];
  53927. break;
  53928. case '==' :
  53929. $newdep['min'] = $dep['version'];
  53930. $newdep['max'] = $dep['version'];
  53931. break;
  53932. }
  53933. if ($type == 'Php') {
  53934. if (!isset($newdep['min'])) {
  53935. $newdep['min'] = '4.4.0';
  53936. }
  53937. if (!isset($newdep['max'])) {
  53938. $newdep['max'] = '6.0.0';
  53939. }
  53940. }
  53941. return array($newdep, $type);
  53942. }
  53943. /**
  53944. * Converts text comparing operators to them sign equivalents
  53945. *
  53946. * Example: 'ge' to '>='
  53947. *
  53948. * @access public
  53949. * @param string Operator
  53950. * @return string Sign equivalent
  53951. */
  53952. static function signOperator($operator)
  53953. {
  53954. switch($operator) {
  53955. case 'lt': return '<';
  53956. case 'le': return '<=';
  53957. case 'gt': return '>';
  53958. case 'ge': return '>=';
  53959. case 'eq': return '==';
  53960. case 'ne': return '!=';
  53961. default:
  53962. return $operator;
  53963. }
  53964. }
  53965. function raiseError($msg)
  53966. {
  53967. if (isset($this->_options['ignore-errors'])) {
  53968. return $this->warning($msg);
  53969. }
  53970. return PEAR::raiseError(sprintf($msg, $this->_registry->parsedPackageNameToString(
  53971. $this->_currentPackage, true)));
  53972. }
  53973. function warning($msg)
  53974. {
  53975. return array(sprintf($msg, $this->_registry->parsedPackageNameToString(
  53976. $this->_currentPackage, true)));
  53977. }
  53978. }
  53979. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Downloader.php��������������������������������������������������������������������0000664�0001750�0001750�00000200731�14720722517�015631� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  53980. /**
  53981. * PEAR_Downloader, the PEAR Installer's download utility class
  53982. *
  53983. * PHP versions 4 and 5
  53984. *
  53985. * @category pear
  53986. * @package PEAR
  53987. * @author Greg Beaver <cellog@php.net>
  53988. * @author Stig Bakken <ssb@php.net>
  53989. * @author Tomas V. V. Cox <cox@idecnet.com>
  53990. * @author Martin Jansen <mj@php.net>
  53991. * @copyright 1997-2009 The Authors
  53992. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  53993. * @link http://pear.php.net/package/PEAR
  53994. * @since File available since Release 1.3.0
  53995. */
  53996. /**
  53997. * Needed for constants, extending
  53998. */
  53999. require_once 'PEAR/Common.php';
  54000. require_once 'PEAR/Proxy.php';
  54001. define('PEAR_INSTALLER_OK', 1);
  54002. define('PEAR_INSTALLER_FAILED', 0);
  54003. define('PEAR_INSTALLER_SKIPPED', -1);
  54004. define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2);
  54005. /**
  54006. * Administration class used to download anything from the internet (PEAR Packages,
  54007. * static URLs, xml files)
  54008. *
  54009. * @category pear
  54010. * @package PEAR
  54011. * @author Greg Beaver <cellog@php.net>
  54012. * @author Stig Bakken <ssb@php.net>
  54013. * @author Tomas V. V. Cox <cox@idecnet.com>
  54014. * @author Martin Jansen <mj@php.net>
  54015. * @copyright 1997-2009 The Authors
  54016. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  54017. * @version Release: 1.10.16
  54018. * @link http://pear.php.net/package/PEAR
  54019. * @since Class available since Release 1.3.0
  54020. */
  54021. class PEAR_Downloader extends PEAR_Common
  54022. {
  54023. /**
  54024. * @var PEAR_Registry
  54025. * @access private
  54026. */
  54027. var $_registry;
  54028. /**
  54029. * Preferred Installation State (snapshot, devel, alpha, beta, stable)
  54030. * @var string|null
  54031. * @access private
  54032. */
  54033. var $_preferredState;
  54034. /**
  54035. * Options from command-line passed to Install.
  54036. *
  54037. * Recognized options:<br />
  54038. * - onlyreqdeps : install all required dependencies as well
  54039. * - alldeps : install all dependencies, including optional
  54040. * - installroot : base relative path to install files in
  54041. * - force : force a download even if warnings would prevent it
  54042. * - nocompress : download uncompressed tarballs
  54043. * - configureoptions : additional configure options
  54044. * @see PEAR_Command_Install
  54045. * @access private
  54046. * @var array
  54047. */
  54048. var $_options;
  54049. /**
  54050. * Downloaded Packages after a call to download().
  54051. *
  54052. * Format of each entry:
  54053. *
  54054. * <code>
  54055. * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
  54056. * 'info' => array() // parsed package.xml
  54057. * );
  54058. * </code>
  54059. * @access private
  54060. * @var array
  54061. */
  54062. var $_downloadedPackages = array();
  54063. /**
  54064. * Packages slated for download.
  54065. *
  54066. * This is used to prevent downloading a package more than once should it be a dependency
  54067. * for two packages to be installed.
  54068. * Format of each entry:
  54069. *
  54070. * <pre>
  54071. * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
  54072. * );
  54073. * </pre>
  54074. * @access private
  54075. * @var array
  54076. */
  54077. var $_toDownload = array();
  54078. /**
  54079. * Array of every package installed, with names lower-cased.
  54080. *
  54081. * Format:
  54082. * <code>
  54083. * array('package1' => 0, 'package2' => 1, );
  54084. * </code>
  54085. * @var array
  54086. */
  54087. var $_installed = array();
  54088. /**
  54089. * @var array
  54090. * @access private
  54091. */
  54092. var $_errorStack = array();
  54093. /**
  54094. * @var boolean
  54095. * @access private
  54096. */
  54097. var $_internalDownload = false;
  54098. /**
  54099. * Temporary variable used in sorting packages by dependency in {@link sortPkgDeps()}
  54100. * @var array
  54101. * @access private
  54102. */
  54103. var $_packageSortTree;
  54104. /**
  54105. * Temporary directory, or configuration value where downloads will occur
  54106. * @var string
  54107. */
  54108. var $_downloadDir;
  54109. /**
  54110. * List of methods that can be called both statically and non-statically.
  54111. * @var array
  54112. */
  54113. protected static $bivalentMethods = array(
  54114. 'setErrorHandling' => true,
  54115. 'raiseError' => true,
  54116. 'throwError' => true,
  54117. 'pushErrorHandling' => true,
  54118. 'popErrorHandling' => true,
  54119. 'downloadHttp' => true,
  54120. );
  54121. /**
  54122. * @param PEAR_Frontend_*
  54123. * @param array
  54124. * @param PEAR_Config
  54125. */
  54126. function __construct($ui = null, $options = array(), $config = null)
  54127. {
  54128. parent::__construct();
  54129. $this->_options = $options;
  54130. if ($config !== null) {
  54131. $this->config = &$config;
  54132. $this->_preferredState = $this->config->get('preferred_state');
  54133. }
  54134. $this->ui = &$ui;
  54135. if (!$this->_preferredState) {
  54136. // don't inadvertently use a non-set preferred_state
  54137. $this->_preferredState = null;
  54138. }
  54139. if ($config !== null) {
  54140. if (isset($this->_options['installroot'])) {
  54141. $this->config->setInstallRoot($this->_options['installroot']);
  54142. }
  54143. $this->_registry = &$config->getRegistry();
  54144. }
  54145. if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
  54146. $this->_installed = $this->_registry->listAllPackages();
  54147. foreach ($this->_installed as $key => $unused) {
  54148. if (!count($unused)) {
  54149. continue;
  54150. }
  54151. $strtolower = function($a) { return strtolower($a); };
  54152. array_walk($this->_installed[$key], $strtolower);
  54153. }
  54154. }
  54155. }
  54156. /**
  54157. * Attempt to discover a channel's remote capabilities from
  54158. * its server name
  54159. * @param string
  54160. * @return boolean
  54161. */
  54162. function discover($channel)
  54163. {
  54164. $this->log(1, 'Attempting to discover channel "' . $channel . '"...');
  54165. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  54166. $callback = $this->ui ? array(&$this, '_downloadCallback') : null;
  54167. if (!class_exists('System')) {
  54168. require_once 'System.php';
  54169. }
  54170. $tmpdir = $this->config->get('temp_dir');
  54171. $tmp = System::mktemp('-d -t "' . $tmpdir . '"');
  54172. $a = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false);
  54173. PEAR::popErrorHandling();
  54174. if (PEAR::isError($a)) {
  54175. // Attempt to fallback to https automatically.
  54176. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  54177. $this->log(1, 'Attempting fallback to https instead of http on channel "' . $channel . '"...');
  54178. $a = $this->downloadHttp('https://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false);
  54179. PEAR::popErrorHandling();
  54180. if (PEAR::isError($a)) {
  54181. return false;
  54182. }
  54183. }
  54184. list($a, $lastmodified) = $a;
  54185. if (!class_exists('PEAR_ChannelFile')) {
  54186. require_once 'PEAR/ChannelFile.php';
  54187. }
  54188. $b = new PEAR_ChannelFile;
  54189. if ($b->fromXmlFile($a)) {
  54190. unlink($a);
  54191. if ($this->config->get('auto_discover')) {
  54192. $this->_registry->addChannel($b, $lastmodified);
  54193. $alias = $b->getName();
  54194. if ($b->getName() == $this->_registry->channelName($b->getAlias())) {
  54195. $alias = $b->getAlias();
  54196. }
  54197. $this->log(1, 'Auto-discovered channel "' . $channel .
  54198. '", alias "' . $alias . '", adding to registry');
  54199. }
  54200. return true;
  54201. }
  54202. unlink($a);
  54203. return false;
  54204. }
  54205. /**
  54206. * For simpler unit-testing
  54207. * @param PEAR_Downloader
  54208. * @return PEAR_Downloader_Package
  54209. */
  54210. function newDownloaderPackage(&$t)
  54211. {
  54212. if (!class_exists('PEAR_Downloader_Package')) {
  54213. require_once 'PEAR/Downloader/Package.php';
  54214. }
  54215. $a = new PEAR_Downloader_Package($t);
  54216. return $a;
  54217. }
  54218. /**
  54219. * For simpler unit-testing
  54220. * @param PEAR_Config
  54221. * @param array
  54222. * @param array
  54223. * @param int
  54224. */
  54225. function &getDependency2Object(&$c, $i, $p, $s)
  54226. {
  54227. if (!class_exists('PEAR_Dependency2')) {
  54228. require_once 'PEAR/Dependency2.php';
  54229. }
  54230. $z = new PEAR_Dependency2($c, $i, $p, $s);
  54231. return $z;
  54232. }
  54233. function &download($params)
  54234. {
  54235. if (!count($params)) {
  54236. $a = array();
  54237. return $a;
  54238. }
  54239. if (!isset($this->_registry)) {
  54240. $this->_registry = &$this->config->getRegistry();
  54241. }
  54242. $channelschecked = array();
  54243. // convert all parameters into PEAR_Downloader_Package objects
  54244. foreach ($params as $i => $param) {
  54245. $params[$i] = $this->newDownloaderPackage($this);
  54246. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  54247. $err = $params[$i]->initialize($param);
  54248. PEAR::staticPopErrorHandling();
  54249. if (!$err) {
  54250. // skip parameters that were missed by preferred_state
  54251. continue;
  54252. }
  54253. if (PEAR::isError($err)) {
  54254. if (!isset($this->_options['soft']) && $err->getMessage() !== '') {
  54255. $this->log(0, $err->getMessage());
  54256. }
  54257. $params[$i] = false;
  54258. if (is_object($param)) {
  54259. $param = $param->getChannel() . '/' . $param->getPackage();
  54260. }
  54261. if (!isset($this->_options['soft'])) {
  54262. $this->log(2, 'Package "' . $param . '" is not valid');
  54263. }
  54264. // Message logged above in a specific verbose mode, passing null to not show up on CLI
  54265. $this->pushError(null, PEAR_INSTALLER_SKIPPED);
  54266. } else {
  54267. do {
  54268. if ($params[$i] && $params[$i]->getType() == 'local') {
  54269. // bug #7090 skip channel.xml check for local packages
  54270. break;
  54271. }
  54272. if ($params[$i] && !isset($channelschecked[$params[$i]->getChannel()]) &&
  54273. !isset($this->_options['offline'])
  54274. ) {
  54275. $channelschecked[$params[$i]->getChannel()] = true;
  54276. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  54277. if (!class_exists('System')) {
  54278. require_once 'System.php';
  54279. }
  54280. $curchannel = $this->_registry->getChannel($params[$i]->getChannel());
  54281. if (PEAR::isError($curchannel)) {
  54282. PEAR::staticPopErrorHandling();
  54283. return $this->raiseError($curchannel);
  54284. }
  54285. if (PEAR::isError($dir = $this->getDownloadDir())) {
  54286. PEAR::staticPopErrorHandling();
  54287. break;
  54288. }
  54289. $mirror = $this->config->get('preferred_mirror', null, $params[$i]->getChannel());
  54290. $url = 'http://' . $mirror . '/channel.xml';
  54291. $a = $this->downloadHttp($url, $this->ui, $dir, null, $curchannel->lastModified());
  54292. PEAR::staticPopErrorHandling();
  54293. if ($a === false) {
  54294. //channel.xml not modified
  54295. break;
  54296. } else if (PEAR::isError($a)) {
  54297. // Attempt fallback to https automatically
  54298. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  54299. $a = $this->downloadHttp('https://' . $mirror .
  54300. '/channel.xml', $this->ui, $dir, null, $curchannel->lastModified());
  54301. PEAR::staticPopErrorHandling();
  54302. if (PEAR::isError($a) || !$a) {
  54303. break;
  54304. }
  54305. }
  54306. $this->log(0, 'WARNING: channel "' . $params[$i]->getChannel() . '" has ' .
  54307. 'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $params[$i]->getChannel() .
  54308. '" to update');
  54309. }
  54310. } while (false);
  54311. if ($params[$i] && !isset($this->_options['downloadonly'])) {
  54312. if (isset($this->_options['packagingroot'])) {
  54313. $checkdir = $this->_prependPath(
  54314. $this->config->get('php_dir', null, $params[$i]->getChannel()),
  54315. $this->_options['packagingroot']);
  54316. } else {
  54317. $checkdir = $this->config->get('php_dir',
  54318. null, $params[$i]->getChannel());
  54319. }
  54320. while ($checkdir && $checkdir != '/' && !file_exists($checkdir)) {
  54321. $checkdir = dirname($checkdir);
  54322. }
  54323. if ($checkdir == '.') {
  54324. $checkdir = '/';
  54325. }
  54326. if (!is_writeable($checkdir)) {
  54327. return PEAR::raiseError('Cannot install, php_dir for channel "' .
  54328. $params[$i]->getChannel() . '" is not writeable by the current user');
  54329. }
  54330. }
  54331. }
  54332. }
  54333. unset($channelschecked);
  54334. PEAR_Downloader_Package::removeDuplicates($params);
  54335. if (!count($params)) {
  54336. $a = array();
  54337. return $a;
  54338. }
  54339. if (!isset($this->_options['nodeps']) && !isset($this->_options['offline'])) {
  54340. $reverify = true;
  54341. while ($reverify) {
  54342. $reverify = false;
  54343. foreach ($params as $i => $param) {
  54344. //PHP Bug 40768 / PEAR Bug #10944
  54345. //Nested foreaches fail in PHP 5.2.1
  54346. key($params);
  54347. $ret = $params[$i]->detectDependencies($params);
  54348. if (PEAR::isError($ret)) {
  54349. $reverify = true;
  54350. $params[$i] = false;
  54351. PEAR_Downloader_Package::removeDuplicates($params);
  54352. if (!isset($this->_options['soft'])) {
  54353. $this->log(0, $ret->getMessage());
  54354. }
  54355. continue 2;
  54356. }
  54357. }
  54358. }
  54359. }
  54360. if (isset($this->_options['offline'])) {
  54361. $this->log(3, 'Skipping dependency download check, --offline specified');
  54362. }
  54363. if (!count($params)) {
  54364. $a = array();
  54365. return $a;
  54366. }
  54367. while (PEAR_Downloader_Package::mergeDependencies($params));
  54368. PEAR_Downloader_Package::removeDuplicates($params, true);
  54369. $errorparams = array();
  54370. if (PEAR_Downloader_Package::detectStupidDuplicates($params, $errorparams)) {
  54371. if (count($errorparams)) {
  54372. foreach ($errorparams as $param) {
  54373. $name = $this->_registry->parsedPackageNameToString($param->getParsedPackage());
  54374. $this->pushError('Duplicate package ' . $name . ' found', PEAR_INSTALLER_FAILED);
  54375. }
  54376. $a = array();
  54377. return $a;
  54378. }
  54379. }
  54380. PEAR_Downloader_Package::removeInstalled($params);
  54381. if (!count($params)) {
  54382. $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
  54383. $a = array();
  54384. return $a;
  54385. }
  54386. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  54387. $err = $this->analyzeDependencies($params);
  54388. PEAR::popErrorHandling();
  54389. if (!count($params)) {
  54390. $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
  54391. $a = array();
  54392. return $a;
  54393. }
  54394. $ret = array();
  54395. $newparams = array();
  54396. if (isset($this->_options['pretend'])) {
  54397. return $params;
  54398. }
  54399. $somefailed = false;
  54400. foreach ($params as $i => $package) {
  54401. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  54402. $pf = &$params[$i]->download();
  54403. PEAR::staticPopErrorHandling();
  54404. if (PEAR::isError($pf)) {
  54405. if (!isset($this->_options['soft'])) {
  54406. $this->log(1, $pf->getMessage());
  54407. $this->log(0, 'Error: cannot download "' .
  54408. $this->_registry->parsedPackageNameToString($package->getParsedPackage(),
  54409. true) .
  54410. '"');
  54411. }
  54412. $somefailed = true;
  54413. continue;
  54414. }
  54415. $newparams[] = &$params[$i];
  54416. $ret[] = array(
  54417. 'file' => $pf->getArchiveFile(),
  54418. 'info' => &$pf,
  54419. 'pkg' => $pf->getPackage()
  54420. );
  54421. }
  54422. if ($somefailed) {
  54423. // remove params that did not download successfully
  54424. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  54425. $err = $this->analyzeDependencies($newparams, true);
  54426. PEAR::popErrorHandling();
  54427. if (!count($newparams)) {
  54428. $this->pushError('Download failed', PEAR_INSTALLER_FAILED);
  54429. $a = array();
  54430. return $a;
  54431. }
  54432. }
  54433. $this->_downloadedPackages = $ret;
  54434. return $newparams;
  54435. }
  54436. /**
  54437. * @param array all packages to be installed
  54438. */
  54439. function analyzeDependencies(&$params, $force = false)
  54440. {
  54441. if (isset($this->_options['downloadonly'])) {
  54442. return;
  54443. }
  54444. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  54445. $redo = true;
  54446. $reset = $hasfailed = $failed = false;
  54447. while ($redo) {
  54448. $redo = false;
  54449. foreach ($params as $i => $param) {
  54450. $deps = $param->getDeps();
  54451. if (!$deps) {
  54452. $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(),
  54453. $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING);
  54454. $send = $param->getPackageFile();
  54455. $installcheck = $depchecker->validatePackage($send, $this, $params);
  54456. if (PEAR::isError($installcheck)) {
  54457. if (!isset($this->_options['soft'])) {
  54458. $this->log(0, $installcheck->getMessage());
  54459. }
  54460. $hasfailed = true;
  54461. $params[$i] = false;
  54462. $reset = true;
  54463. $redo = true;
  54464. $failed = false;
  54465. PEAR_Downloader_Package::removeDuplicates($params);
  54466. continue 2;
  54467. }
  54468. continue;
  54469. }
  54470. if (!$reset && $param->alreadyValidated() && !$force) {
  54471. continue;
  54472. }
  54473. if (count($deps)) {
  54474. $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(),
  54475. $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING);
  54476. $send = $param->getPackageFile();
  54477. if ($send === null) {
  54478. $send = $param->getDownloadURL();
  54479. }
  54480. $installcheck = $depchecker->validatePackage($send, $this, $params);
  54481. if (PEAR::isError($installcheck)) {
  54482. if (!isset($this->_options['soft'])) {
  54483. $this->log(0, $installcheck->getMessage());
  54484. }
  54485. $hasfailed = true;
  54486. $params[$i] = false;
  54487. $reset = true;
  54488. $redo = true;
  54489. $failed = false;
  54490. PEAR_Downloader_Package::removeDuplicates($params);
  54491. continue 2;
  54492. }
  54493. $failed = false;
  54494. if (isset($deps['required']) && is_array($deps['required'])) {
  54495. foreach ($deps['required'] as $type => $dep) {
  54496. // note: Dependency2 will never return a PEAR_Error if ignore-errors
  54497. // is specified, so soft is needed to turn off logging
  54498. if (!isset($dep[0])) {
  54499. if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($dep,
  54500. true, $params))) {
  54501. $failed = true;
  54502. if (!isset($this->_options['soft'])) {
  54503. $this->log(0, $e->getMessage());
  54504. }
  54505. } elseif (is_array($e) && !$param->alreadyValidated()) {
  54506. if (!isset($this->_options['soft'])) {
  54507. $this->log(0, $e[0]);
  54508. }
  54509. }
  54510. } else {
  54511. foreach ($dep as $d) {
  54512. if (PEAR::isError($e =
  54513. $depchecker->{"validate{$type}Dependency"}($d,
  54514. true, $params))) {
  54515. $failed = true;
  54516. if (!isset($this->_options['soft'])) {
  54517. $this->log(0, $e->getMessage());
  54518. }
  54519. } elseif (is_array($e) && !$param->alreadyValidated()) {
  54520. if (!isset($this->_options['soft'])) {
  54521. $this->log(0, $e[0]);
  54522. }
  54523. }
  54524. }
  54525. }
  54526. }
  54527. if (isset($deps['optional']) && is_array($deps['optional'])) {
  54528. foreach ($deps['optional'] as $type => $dep) {
  54529. if (!isset($dep[0])) {
  54530. if (PEAR::isError($e =
  54531. $depchecker->{"validate{$type}Dependency"}($dep,
  54532. false, $params))) {
  54533. $failed = true;
  54534. if (!isset($this->_options['soft'])) {
  54535. $this->log(0, $e->getMessage());
  54536. }
  54537. } elseif (is_array($e) && !$param->alreadyValidated()) {
  54538. if (!isset($this->_options['soft'])) {
  54539. $this->log(0, $e[0]);
  54540. }
  54541. }
  54542. } else {
  54543. foreach ($dep as $d) {
  54544. if (PEAR::isError($e =
  54545. $depchecker->{"validate{$type}Dependency"}($d,
  54546. false, $params))) {
  54547. $failed = true;
  54548. if (!isset($this->_options['soft'])) {
  54549. $this->log(0, $e->getMessage());
  54550. }
  54551. } elseif (is_array($e) && !$param->alreadyValidated()) {
  54552. if (!isset($this->_options['soft'])) {
  54553. $this->log(0, $e[0]);
  54554. }
  54555. }
  54556. }
  54557. }
  54558. }
  54559. }
  54560. $groupname = $param->getGroup();
  54561. if (isset($deps['group']) && $groupname) {
  54562. if (!isset($deps['group'][0])) {
  54563. $deps['group'] = array($deps['group']);
  54564. }
  54565. $found = false;
  54566. foreach ($deps['group'] as $group) {
  54567. if ($group['attribs']['name'] == $groupname) {
  54568. $found = true;
  54569. break;
  54570. }
  54571. }
  54572. if ($found) {
  54573. unset($group['attribs']);
  54574. foreach ($group as $type => $dep) {
  54575. if (!isset($dep[0])) {
  54576. if (PEAR::isError($e =
  54577. $depchecker->{"validate{$type}Dependency"}($dep,
  54578. false, $params))) {
  54579. $failed = true;
  54580. if (!isset($this->_options['soft'])) {
  54581. $this->log(0, $e->getMessage());
  54582. }
  54583. } elseif (is_array($e) && !$param->alreadyValidated()) {
  54584. if (!isset($this->_options['soft'])) {
  54585. $this->log(0, $e[0]);
  54586. }
  54587. }
  54588. } else {
  54589. foreach ($dep as $d) {
  54590. if (PEAR::isError($e =
  54591. $depchecker->{"validate{$type}Dependency"}($d,
  54592. false, $params))) {
  54593. $failed = true;
  54594. if (!isset($this->_options['soft'])) {
  54595. $this->log(0, $e->getMessage());
  54596. }
  54597. } elseif (is_array($e) && !$param->alreadyValidated()) {
  54598. if (!isset($this->_options['soft'])) {
  54599. $this->log(0, $e[0]);
  54600. }
  54601. }
  54602. }
  54603. }
  54604. }
  54605. }
  54606. }
  54607. } else {
  54608. foreach ($deps as $dep) {
  54609. if (PEAR::isError($e = $depchecker->validateDependency1($dep, $params))) {
  54610. $failed = true;
  54611. if (!isset($this->_options['soft'])) {
  54612. $this->log(0, $e->getMessage());
  54613. }
  54614. } elseif (is_array($e) && !$param->alreadyValidated()) {
  54615. if (!isset($this->_options['soft'])) {
  54616. $this->log(0, $e[0]);
  54617. }
  54618. }
  54619. }
  54620. }
  54621. $params[$i]->setValidated();
  54622. }
  54623. if ($failed) {
  54624. $hasfailed = true;
  54625. $params[$i] = false;
  54626. $reset = true;
  54627. $redo = true;
  54628. $failed = false;
  54629. PEAR_Downloader_Package::removeDuplicates($params);
  54630. continue 2;
  54631. }
  54632. }
  54633. }
  54634. PEAR::staticPopErrorHandling();
  54635. if ($hasfailed && (isset($this->_options['ignore-errors']) ||
  54636. isset($this->_options['nodeps']))) {
  54637. // this is probably not needed, but just in case
  54638. if (!isset($this->_options['soft'])) {
  54639. $this->log(0, 'WARNING: dependencies failed');
  54640. }
  54641. }
  54642. }
  54643. /**
  54644. * Retrieve the directory that downloads will happen in
  54645. * @access private
  54646. * @return string
  54647. */
  54648. function getDownloadDir()
  54649. {
  54650. if (isset($this->_downloadDir)) {
  54651. return $this->_downloadDir;
  54652. }
  54653. $downloaddir = $this->config->get('download_dir');
  54654. if (empty($downloaddir) || (is_dir($downloaddir) && !is_writable($downloaddir))) {
  54655. if (is_dir($downloaddir) && !is_writable($downloaddir)) {
  54656. $this->log(0, 'WARNING: configuration download directory "' . $downloaddir .
  54657. '" is not writeable. Change download_dir config variable to ' .
  54658. 'a writeable dir to avoid this warning');
  54659. }
  54660. if (!class_exists('System')) {
  54661. require_once 'System.php';
  54662. }
  54663. if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
  54664. return $downloaddir;
  54665. }
  54666. $this->log(3, '+ tmp dir created at ' . $downloaddir);
  54667. }
  54668. if (!is_writable($downloaddir)) {
  54669. if (PEAR::isError(System::mkdir(array('-p', $downloaddir))) ||
  54670. !is_writable($downloaddir)) {
  54671. return PEAR::raiseError('download directory "' . $downloaddir .
  54672. '" is not writeable. Change download_dir config variable to ' .
  54673. 'a writeable dir');
  54674. }
  54675. }
  54676. return $this->_downloadDir = $downloaddir;
  54677. }
  54678. function setDownloadDir($dir)
  54679. {
  54680. if (!@is_writable($dir)) {
  54681. if (PEAR::isError(System::mkdir(array('-p', $dir)))) {
  54682. return PEAR::raiseError('download directory "' . $dir .
  54683. '" is not writeable. Change download_dir config variable to ' .
  54684. 'a writeable dir');
  54685. }
  54686. }
  54687. $this->_downloadDir = $dir;
  54688. }
  54689. function configSet($key, $value, $layer = 'user', $channel = false)
  54690. {
  54691. $this->config->set($key, $value, $layer, $channel);
  54692. $this->_preferredState = $this->config->get('preferred_state', null, $channel);
  54693. if (!$this->_preferredState) {
  54694. // don't inadvertently use a non-set preferred_state
  54695. $this->_preferredState = null;
  54696. }
  54697. }
  54698. function setOptions($options)
  54699. {
  54700. $this->_options = $options;
  54701. }
  54702. function getOptions()
  54703. {
  54704. return $this->_options;
  54705. }
  54706. /**
  54707. * @param array output of {@link parsePackageName()}
  54708. * @access private
  54709. */
  54710. function _getPackageDownloadUrl($parr)
  54711. {
  54712. $curchannel = $this->config->get('default_channel');
  54713. $this->configSet('default_channel', $parr['channel']);
  54714. // getDownloadURL returns an array. On error, it only contains information
  54715. // on the latest release as array(version, info). On success it contains
  54716. // array(version, info, download url string)
  54717. $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
  54718. if (!$this->_registry->channelExists($parr['channel'])) {
  54719. do {
  54720. if ($this->config->get('auto_discover') && $this->discover($parr['channel'])) {
  54721. break;
  54722. }
  54723. $this->configSet('default_channel', $curchannel);
  54724. return PEAR::raiseError('Unknown remote channel: ' . $parr['channel']);
  54725. } while (false);
  54726. }
  54727. $chan = $this->_registry->getChannel($parr['channel']);
  54728. if (PEAR::isError($chan)) {
  54729. return $chan;
  54730. }
  54731. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  54732. $version = $this->_registry->packageInfo($parr['package'], 'version', $parr['channel']);
  54733. $stability = $this->_registry->packageInfo($parr['package'], 'stability', $parr['channel']);
  54734. // package is installed - use the installed release stability level
  54735. if (!isset($parr['state']) && $stability !== null) {
  54736. $state = $stability['release'];
  54737. }
  54738. PEAR::staticPopErrorHandling();
  54739. $base2 = false;
  54740. $preferred_mirror = $this->config->get('preferred_mirror');
  54741. if (!$chan->supportsREST($preferred_mirror) ||
  54742. (
  54743. !($base2 = $chan->getBaseURL('REST1.3', $preferred_mirror))
  54744. &&
  54745. !($base = $chan->getBaseURL('REST1.0', $preferred_mirror))
  54746. )
  54747. ) {
  54748. return $this->raiseError($parr['channel'] . ' is using an unsupported protocol - This should never happen. Use --force to continue');
  54749. }
  54750. if ($base2) {
  54751. $rest = &$this->config->getREST('1.3', $this->_options);
  54752. $base = $base2;
  54753. } else {
  54754. $rest = &$this->config->getREST('1.0', $this->_options);
  54755. }
  54756. $downloadVersion = false;
  54757. if (!isset($parr['version']) && !isset($parr['state']) && $version
  54758. && !PEAR::isError($version)
  54759. && !isset($this->_options['downloadonly'])
  54760. ) {
  54761. $downloadVersion = $version;
  54762. }
  54763. $url = $rest->getDownloadURL($base, $parr, $state, $downloadVersion, $chan->getName());
  54764. if (PEAR::isError($url)) {
  54765. $this->configSet('default_channel', $curchannel);
  54766. return $url;
  54767. }
  54768. if ($parr['channel'] != $curchannel) {
  54769. $this->configSet('default_channel', $curchannel);
  54770. }
  54771. if (!is_array($url)) {
  54772. return $url;
  54773. }
  54774. $url['raw'] = false; // no checking is necessary for REST
  54775. if (!is_array($url['info'])) {
  54776. return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
  54777. 'this should never happen');
  54778. }
  54779. if (!isset($this->_options['force']) &&
  54780. !isset($this->_options['downloadonly']) &&
  54781. $version &&
  54782. !PEAR::isError($version) &&
  54783. !isset($parr['group'])
  54784. ) {
  54785. if (version_compare($version, $url['version'], '=')) {
  54786. return PEAR::raiseError($this->_registry->parsedPackageNameToString(
  54787. $parr, true) . ' is already installed and is the same as the ' .
  54788. 'released version ' . $url['version'], -976);
  54789. }
  54790. if (version_compare($version, $url['version'], '>')) {
  54791. return PEAR::raiseError($this->_registry->parsedPackageNameToString(
  54792. $parr, true) . ' is already installed and is newer than detected ' .
  54793. 'released version ' . $url['version'], -976);
  54794. }
  54795. }
  54796. if (isset($url['info']['required']) || $url['compatible']) {
  54797. require_once 'PEAR/PackageFile/v2.php';
  54798. $pf = new PEAR_PackageFile_v2;
  54799. $pf->setRawChannel($parr['channel']);
  54800. if ($url['compatible']) {
  54801. $pf->setRawCompatible($url['compatible']);
  54802. }
  54803. } else {
  54804. require_once 'PEAR/PackageFile/v1.php';
  54805. $pf = new PEAR_PackageFile_v1;
  54806. }
  54807. $pf->setRawPackage($url['package']);
  54808. $pf->setDeps($url['info']);
  54809. if ($url['compatible']) {
  54810. $pf->setCompatible($url['compatible']);
  54811. }
  54812. $pf->setRawState($url['stability']);
  54813. $url['info'] = &$pf;
  54814. if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
  54815. $ext = '.tar';
  54816. } else {
  54817. $ext = '.tgz';
  54818. }
  54819. if (is_array($url) && isset($url['url'])) {
  54820. $url['url'] .= $ext;
  54821. }
  54822. return $url;
  54823. }
  54824. /**
  54825. * @param array dependency array
  54826. * @access private
  54827. */
  54828. function _getDepPackageDownloadUrl($dep, $parr)
  54829. {
  54830. $xsdversion = isset($dep['rel']) ? '1.0' : '2.0';
  54831. $curchannel = $this->config->get('default_channel');
  54832. if (isset($dep['uri'])) {
  54833. $xsdversion = '2.0';
  54834. $chan = $this->_registry->getChannel('__uri');
  54835. if (PEAR::isError($chan)) {
  54836. return $chan;
  54837. }
  54838. $version = $this->_registry->packageInfo($dep['name'], 'version', '__uri');
  54839. $this->configSet('default_channel', '__uri');
  54840. } else {
  54841. if (isset($dep['channel'])) {
  54842. $remotechannel = $dep['channel'];
  54843. } else {
  54844. $remotechannel = 'pear.php.net';
  54845. }
  54846. if (!$this->_registry->channelExists($remotechannel)) {
  54847. do {
  54848. if ($this->config->get('auto_discover')) {
  54849. if ($this->discover($remotechannel)) {
  54850. break;
  54851. }
  54852. }
  54853. return PEAR::raiseError('Unknown remote channel: ' . $remotechannel);
  54854. } while (false);
  54855. }
  54856. $chan = $this->_registry->getChannel($remotechannel);
  54857. if (PEAR::isError($chan)) {
  54858. return $chan;
  54859. }
  54860. $version = $this->_registry->packageInfo($dep['name'], 'version', $remotechannel);
  54861. $this->configSet('default_channel', $remotechannel);
  54862. }
  54863. $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
  54864. if (isset($parr['state']) && isset($parr['version'])) {
  54865. unset($parr['state']);
  54866. }
  54867. if (isset($dep['uri'])) {
  54868. $info = $this->newDownloaderPackage($this);
  54869. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  54870. $err = $info->initialize($dep);
  54871. PEAR::staticPopErrorHandling();
  54872. if (!$err) {
  54873. // skip parameters that were missed by preferred_state
  54874. return PEAR::raiseError('Cannot initialize dependency');
  54875. }
  54876. if (PEAR::isError($err)) {
  54877. if (!isset($this->_options['soft'])) {
  54878. $this->log(0, $err->getMessage());
  54879. }
  54880. if (is_object($info)) {
  54881. $param = $info->getChannel() . '/' . $info->getPackage();
  54882. }
  54883. return PEAR::raiseError('Package "' . $param . '" is not valid');
  54884. }
  54885. return $info;
  54886. } elseif ($chan->supportsREST($this->config->get('preferred_mirror'))
  54887. &&
  54888. (
  54889. ($base2 = $chan->getBaseURL('REST1.3', $this->config->get('preferred_mirror')))
  54890. ||
  54891. ($base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror')))
  54892. )
  54893. ) {
  54894. if ($base2) {
  54895. $base = $base2;
  54896. $rest = &$this->config->getREST('1.3', $this->_options);
  54897. } else {
  54898. $rest = &$this->config->getREST('1.0', $this->_options);
  54899. }
  54900. $url = $rest->getDepDownloadURL($base, $xsdversion, $dep, $parr,
  54901. $state, $version, $chan->getName());
  54902. if (PEAR::isError($url)) {
  54903. return $url;
  54904. }
  54905. if ($parr['channel'] != $curchannel) {
  54906. $this->configSet('default_channel', $curchannel);
  54907. }
  54908. if (!is_array($url)) {
  54909. return $url;
  54910. }
  54911. $url['raw'] = false; // no checking is necessary for REST
  54912. if (!is_array($url['info'])) {
  54913. return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
  54914. 'this should never happen');
  54915. }
  54916. if (isset($url['info']['required'])) {
  54917. if (!class_exists('PEAR_PackageFile_v2')) {
  54918. require_once 'PEAR/PackageFile/v2.php';
  54919. }
  54920. $pf = new PEAR_PackageFile_v2;
  54921. $pf->setRawChannel($remotechannel);
  54922. } else {
  54923. if (!class_exists('PEAR_PackageFile_v1')) {
  54924. require_once 'PEAR/PackageFile/v1.php';
  54925. }
  54926. $pf = new PEAR_PackageFile_v1;
  54927. }
  54928. $pf->setRawPackage($url['package']);
  54929. $pf->setDeps($url['info']);
  54930. if ($url['compatible']) {
  54931. $pf->setCompatible($url['compatible']);
  54932. }
  54933. $pf->setRawState($url['stability']);
  54934. $url['info'] = &$pf;
  54935. if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
  54936. $ext = '.tar';
  54937. } else {
  54938. $ext = '.tgz';
  54939. }
  54940. if (is_array($url) && isset($url['url'])) {
  54941. $url['url'] .= $ext;
  54942. }
  54943. return $url;
  54944. }
  54945. return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.');
  54946. }
  54947. /**
  54948. * @deprecated in favor of _getPackageDownloadUrl
  54949. */
  54950. function getPackageDownloadUrl($package, $version = null, $channel = false)
  54951. {
  54952. if ($version) {
  54953. $package .= "-$version";
  54954. }
  54955. if ($this === null || $this->_registry === null) {
  54956. $package = "http://pear.php.net/get/$package";
  54957. } else {
  54958. $chan = $this->_registry->getChannel($channel);
  54959. if (PEAR::isError($chan)) {
  54960. return '';
  54961. }
  54962. $package = "http://" . $chan->getServer() . "/get/$package";
  54963. }
  54964. if (!extension_loaded("zlib")) {
  54965. $package .= '?uncompress=yes';
  54966. }
  54967. return $package;
  54968. }
  54969. /**
  54970. * Retrieve a list of downloaded packages after a call to {@link download()}.
  54971. *
  54972. * Also resets the list of downloaded packages.
  54973. * @return array
  54974. */
  54975. function getDownloadedPackages()
  54976. {
  54977. $ret = $this->_downloadedPackages;
  54978. $this->_downloadedPackages = array();
  54979. $this->_toDownload = array();
  54980. return $ret;
  54981. }
  54982. function _downloadCallback($msg, $params = null)
  54983. {
  54984. switch ($msg) {
  54985. case 'saveas':
  54986. $this->log(1, "downloading $params ...");
  54987. break;
  54988. case 'done':
  54989. $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes');
  54990. break;
  54991. case 'bytesread':
  54992. static $bytes;
  54993. if (empty($bytes)) {
  54994. $bytes = 0;
  54995. }
  54996. if (!($bytes % 10240)) {
  54997. $this->log(1, '.', false);
  54998. }
  54999. $bytes += $params;
  55000. break;
  55001. case 'start':
  55002. if($params[1] == -1) {
  55003. $length = "Unknown size";
  55004. } else {
  55005. $length = number_format($params[1], 0, '', ',')." bytes";
  55006. }
  55007. $this->log(1, "Starting to download {$params[0]} ($length)");
  55008. break;
  55009. }
  55010. if (method_exists($this->ui, '_downloadCallback'))
  55011. $this->ui->_downloadCallback($msg, $params);
  55012. }
  55013. function _prependPath($path, $prepend)
  55014. {
  55015. if (strlen($prepend) > 0) {
  55016. if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
  55017. if (preg_match('/^[a-z]:/i', $prepend)) {
  55018. $prepend = substr($prepend, 2);
  55019. } elseif ($prepend[0] != '\\') {
  55020. $prepend = "\\$prepend";
  55021. }
  55022. $path = substr($path, 0, 2) . $prepend . substr($path, 2);
  55023. } else {
  55024. $path = $prepend . $path;
  55025. }
  55026. }
  55027. return $path;
  55028. }
  55029. /**
  55030. * @param string
  55031. * @param integer
  55032. */
  55033. function pushError($errmsg, $code = -1)
  55034. {
  55035. array_push($this->_errorStack, array($errmsg, $code));
  55036. }
  55037. function getErrorMsgs()
  55038. {
  55039. $msgs = array();
  55040. $errs = $this->_errorStack;
  55041. foreach ($errs as $err) {
  55042. $msgs[] = $err[0];
  55043. }
  55044. $this->_errorStack = array();
  55045. return $msgs;
  55046. }
  55047. /**
  55048. * for BC
  55049. *
  55050. * @deprecated
  55051. */
  55052. function sortPkgDeps(&$packages, $uninstall = false)
  55053. {
  55054. $uninstall ?
  55055. $this->sortPackagesForUninstall($packages) :
  55056. $this->sortPackagesForInstall($packages);
  55057. }
  55058. /**
  55059. * Sort a list of arrays of array(downloaded packagefilename) by dependency.
  55060. *
  55061. * This uses the topological sort method from graph theory, and the
  55062. * Structures_Graph package to properly sort dependencies for installation.
  55063. * @param array an array of downloaded PEAR_Downloader_Packages
  55064. * @return array array of array(packagefilename, package.xml contents)
  55065. */
  55066. function sortPackagesForInstall(&$packages)
  55067. {
  55068. require_once 'Structures/Graph.php';
  55069. require_once 'Structures/Graph/Node.php';
  55070. require_once 'Structures/Graph/Manipulator/TopologicalSorter.php';
  55071. $depgraph = new Structures_Graph(true);
  55072. $nodes = array();
  55073. $reg = &$this->config->getRegistry();
  55074. foreach ($packages as $i => $package) {
  55075. $pname = $reg->parsedPackageNameToString(
  55076. array(
  55077. 'channel' => $package->getChannel(),
  55078. 'package' => strtolower($package->getPackage()),
  55079. ));
  55080. $nodes[$pname] = new Structures_Graph_Node;
  55081. $nodes[$pname]->setData($packages[$i]);
  55082. $depgraph->addNode($nodes[$pname]);
  55083. }
  55084. $deplinks = array();
  55085. foreach ($nodes as $package => $node) {
  55086. $pf = &$node->getData();
  55087. $pdeps = $pf->getDeps(true);
  55088. if (!$pdeps) {
  55089. continue;
  55090. }
  55091. if ($pf->getPackagexmlVersion() == '1.0') {
  55092. foreach ($pdeps as $dep) {
  55093. if ($dep['type'] != 'pkg' ||
  55094. (isset($dep['optional']) && $dep['optional'] == 'yes')) {
  55095. continue;
  55096. }
  55097. $dname = $reg->parsedPackageNameToString(
  55098. array(
  55099. 'channel' => 'pear.php.net',
  55100. 'package' => strtolower($dep['name']),
  55101. ));
  55102. if (isset($nodes[$dname])) {
  55103. if (!isset($deplinks[$dname])) {
  55104. $deplinks[$dname] = array();
  55105. }
  55106. $deplinks[$dname][$package] = 1;
  55107. // dependency is in installed packages
  55108. continue;
  55109. }
  55110. $dname = $reg->parsedPackageNameToString(
  55111. array(
  55112. 'channel' => 'pecl.php.net',
  55113. 'package' => strtolower($dep['name']),
  55114. ));
  55115. if (isset($nodes[$dname])) {
  55116. if (!isset($deplinks[$dname])) {
  55117. $deplinks[$dname] = array();
  55118. }
  55119. $deplinks[$dname][$package] = 1;
  55120. // dependency is in installed packages
  55121. continue;
  55122. }
  55123. }
  55124. } else {
  55125. // the only ordering we care about is:
  55126. // 1) subpackages must be installed before packages that depend on them
  55127. // 2) required deps must be installed before packages that depend on them
  55128. if (isset($pdeps['required']['subpackage'])) {
  55129. $t = $pdeps['required']['subpackage'];
  55130. if (!isset($t[0])) {
  55131. $t = array($t);
  55132. }
  55133. $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
  55134. }
  55135. if (isset($pdeps['group'])) {
  55136. if (!isset($pdeps['group'][0])) {
  55137. $pdeps['group'] = array($pdeps['group']);
  55138. }
  55139. foreach ($pdeps['group'] as $group) {
  55140. if (isset($group['subpackage'])) {
  55141. $t = $group['subpackage'];
  55142. if (!isset($t[0])) {
  55143. $t = array($t);
  55144. }
  55145. $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
  55146. }
  55147. }
  55148. }
  55149. if (isset($pdeps['optional']['subpackage'])) {
  55150. $t = $pdeps['optional']['subpackage'];
  55151. if (!isset($t[0])) {
  55152. $t = array($t);
  55153. }
  55154. $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
  55155. }
  55156. if (isset($pdeps['required']['package'])) {
  55157. $t = $pdeps['required']['package'];
  55158. if (!isset($t[0])) {
  55159. $t = array($t);
  55160. }
  55161. $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
  55162. }
  55163. if (isset($pdeps['group'])) {
  55164. if (!isset($pdeps['group'][0])) {
  55165. $pdeps['group'] = array($pdeps['group']);
  55166. }
  55167. foreach ($pdeps['group'] as $group) {
  55168. if (isset($group['package'])) {
  55169. $t = $group['package'];
  55170. if (!isset($t[0])) {
  55171. $t = array($t);
  55172. }
  55173. $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
  55174. }
  55175. }
  55176. }
  55177. }
  55178. }
  55179. $this->_detectDepCycle($deplinks);
  55180. foreach ($deplinks as $dependent => $parents) {
  55181. foreach ($parents as $parent => $unused) {
  55182. $nodes[$dependent]->connectTo($nodes[$parent]);
  55183. }
  55184. }
  55185. $installOrder = Structures_Graph_Manipulator_TopologicalSorter::sort($depgraph);
  55186. $ret = array();
  55187. for ($i = 0, $count = count($installOrder); $i < $count; $i++) {
  55188. foreach ($installOrder[$i] as $index => $sortedpackage) {
  55189. $data = &$installOrder[$i][$index]->getData();
  55190. $ret[] = &$nodes[$reg->parsedPackageNameToString(
  55191. array(
  55192. 'channel' => $data->getChannel(),
  55193. 'package' => strtolower($data->getPackage()),
  55194. ))]->getData();
  55195. }
  55196. }
  55197. $packages = $ret;
  55198. return;
  55199. }
  55200. /**
  55201. * Detect recursive links between dependencies and break the cycles
  55202. *
  55203. * @param array
  55204. * @access private
  55205. */
  55206. function _detectDepCycle(&$deplinks)
  55207. {
  55208. do {
  55209. $keepgoing = false;
  55210. foreach ($deplinks as $dep => $parents) {
  55211. foreach ($parents as $parent => $unused) {
  55212. // reset the parent cycle detector
  55213. $this->_testCycle(null, null, null);
  55214. if ($this->_testCycle($dep, $deplinks, $parent)) {
  55215. $keepgoing = true;
  55216. unset($deplinks[$dep][$parent]);
  55217. if (count($deplinks[$dep]) == 0) {
  55218. unset($deplinks[$dep]);
  55219. }
  55220. continue 3;
  55221. }
  55222. }
  55223. }
  55224. } while ($keepgoing);
  55225. }
  55226. function _testCycle($test, $deplinks, $dep)
  55227. {
  55228. static $visited = array();
  55229. if ($test === null) {
  55230. $visited = array();
  55231. return;
  55232. }
  55233. // this happens when a parent has a dep cycle on another dependency
  55234. // but the child is not part of the cycle
  55235. if (isset($visited[$dep])) {
  55236. return false;
  55237. }
  55238. $visited[$dep] = 1;
  55239. if ($test == $dep) {
  55240. return true;
  55241. }
  55242. if (isset($deplinks[$dep])) {
  55243. if (in_array($test, array_keys($deplinks[$dep]), true)) {
  55244. return true;
  55245. }
  55246. foreach ($deplinks[$dep] as $parent => $unused) {
  55247. if ($this->_testCycle($test, $deplinks, $parent)) {
  55248. return true;
  55249. }
  55250. }
  55251. }
  55252. return false;
  55253. }
  55254. /**
  55255. * Set up the dependency for installation parsing
  55256. *
  55257. * @param array $t dependency information
  55258. * @param PEAR_Registry $reg
  55259. * @param array $deplinks list of dependency links already established
  55260. * @param array $nodes all existing package nodes
  55261. * @param string $package parent package name
  55262. * @access private
  55263. */
  55264. function _setupGraph($t, $reg, &$deplinks, &$nodes, $package)
  55265. {
  55266. foreach ($t as $dep) {
  55267. $depchannel = !isset($dep['channel']) ? '__uri': $dep['channel'];
  55268. $dname = $reg->parsedPackageNameToString(
  55269. array(
  55270. 'channel' => $depchannel,
  55271. 'package' => strtolower($dep['name']),
  55272. ));
  55273. if (isset($nodes[$dname])) {
  55274. if (!isset($deplinks[$dname])) {
  55275. $deplinks[$dname] = array();
  55276. }
  55277. $deplinks[$dname][$package] = 1;
  55278. }
  55279. }
  55280. }
  55281. function _dependsOn($a, $b)
  55282. {
  55283. return $this->_checkDepTree(strtolower($a->getChannel()), strtolower($a->getPackage()), $b);
  55284. }
  55285. function _checkDepTree($channel, $package, $b, $checked = array())
  55286. {
  55287. $checked[$channel][$package] = true;
  55288. if (!isset($this->_depTree[$channel][$package])) {
  55289. return false;
  55290. }
  55291. if (isset($this->_depTree[$channel][$package][strtolower($b->getChannel())]
  55292. [strtolower($b->getPackage())])) {
  55293. return true;
  55294. }
  55295. foreach ($this->_depTree[$channel][$package] as $ch => $packages) {
  55296. foreach ($packages as $pa => $true) {
  55297. if ($this->_checkDepTree($ch, $pa, $b, $checked)) {
  55298. return true;
  55299. }
  55300. }
  55301. }
  55302. return false;
  55303. }
  55304. function _sortInstall($a, $b)
  55305. {
  55306. if (!$a->getDeps() && !$b->getDeps()) {
  55307. return 0; // neither package has dependencies, order is insignificant
  55308. }
  55309. if ($a->getDeps() && !$b->getDeps()) {
  55310. return 1; // $a must be installed after $b because $a has dependencies
  55311. }
  55312. if (!$a->getDeps() && $b->getDeps()) {
  55313. return -1; // $b must be installed after $a because $b has dependencies
  55314. }
  55315. // both packages have dependencies
  55316. if ($this->_dependsOn($a, $b)) {
  55317. return 1;
  55318. }
  55319. if ($this->_dependsOn($b, $a)) {
  55320. return -1;
  55321. }
  55322. return 0;
  55323. }
  55324. /**
  55325. * Download a file through HTTP. Considers suggested file name in
  55326. * Content-disposition: header and can run a callback function for
  55327. * different events. The callback will be called with two
  55328. * parameters: the callback type, and parameters. The implemented
  55329. * callback types are:
  55330. *
  55331. * 'setup' called at the very beginning, parameter is a UI object
  55332. * that should be used for all output
  55333. * 'message' the parameter is a string with an informational message
  55334. * 'saveas' may be used to save with a different file name, the
  55335. * parameter is the filename that is about to be used.
  55336. * If a 'saveas' callback returns a non-empty string,
  55337. * that file name will be used as the filename instead.
  55338. * Note that $save_dir will not be affected by this, only
  55339. * the basename of the file.
  55340. * 'start' download is starting, parameter is number of bytes
  55341. * that are expected, or -1 if unknown
  55342. * 'bytesread' parameter is the number of bytes read so far
  55343. * 'done' download is complete, parameter is the total number
  55344. * of bytes read
  55345. * 'connfailed' if the TCP/SSL connection fails, this callback is called
  55346. * with array(host,port,errno,errmsg)
  55347. * 'writefailed' if writing to disk fails, this callback is called
  55348. * with array(destfile,errmsg)
  55349. *
  55350. * If an HTTP proxy has been configured (http_proxy PEAR_Config
  55351. * setting), the proxy will be used.
  55352. *
  55353. * @param string $url the URL to download
  55354. * @param object $ui PEAR_Frontend_* instance
  55355. * @param object $config PEAR_Config instance
  55356. * @param string $save_dir directory to save file in
  55357. * @param mixed $callback function/method to call for status
  55358. * updates
  55359. * @param false|string|array $lastmodified header values to check against for caching
  55360. * use false to return the header values from this download
  55361. * @param false|array $accept Accept headers to send
  55362. * @param false|string $channel Channel to use for retrieving authentication
  55363. * @return mixed Returns the full path of the downloaded file or a PEAR
  55364. * error on failure. If the error is caused by
  55365. * socket-related errors, the error object will
  55366. * have the fsockopen error code available through
  55367. * getCode(). If caching is requested, then return the header
  55368. * values.
  55369. * If $lastmodified was given and the there are no changes,
  55370. * boolean false is returned.
  55371. *
  55372. * @access public
  55373. */
  55374. public static function _downloadHttp(
  55375. $object, $url, &$ui, $save_dir = '.', $callback = null, $lastmodified = null,
  55376. $accept = false, $channel = false
  55377. ) {
  55378. static $redirect = 0;
  55379. // always reset , so we are clean case of error
  55380. $wasredirect = $redirect;
  55381. $redirect = 0;
  55382. if ($callback) {
  55383. call_user_func($callback, 'setup', array(&$ui));
  55384. }
  55385. $info = parse_url($url);
  55386. if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) {
  55387. return PEAR::raiseError('Cannot download non-http URL "' . $url . '"');
  55388. }
  55389. if (!isset($info['host'])) {
  55390. return PEAR::raiseError('Cannot download from non-URL "' . $url . '"');
  55391. }
  55392. $host = isset($info['host']) ? $info['host'] : null;
  55393. $port = isset($info['port']) ? $info['port'] : null;
  55394. $path = isset($info['path']) ? $info['path'] : null;
  55395. if ($object !== null) {
  55396. $config = $object->config;
  55397. } else {
  55398. $config = &PEAR_Config::singleton();
  55399. }
  55400. $proxy = new PEAR_Proxy($config);
  55401. if ($proxy->isProxyConfigured() && $callback) {
  55402. call_user_func($callback, 'message', "Using HTTP proxy $host:$port");
  55403. }
  55404. if (empty($port)) {
  55405. $port = (isset($info['scheme']) && $info['scheme'] == 'https') ? 443 : 80;
  55406. }
  55407. $scheme = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http';
  55408. $secure = ($scheme == 'https');
  55409. $fp = $proxy->openSocket($host, $port, $secure);
  55410. if (PEAR::isError($fp)) {
  55411. if ($callback) {
  55412. $errno = $fp->getCode();
  55413. $errstr = $fp->getMessage();
  55414. call_user_func($callback, 'connfailed', array($host, $port,
  55415. $errno, $errstr));
  55416. }
  55417. return $fp;
  55418. }
  55419. $requestPath = $path;
  55420. if ($proxy->isProxyConfigured()) {
  55421. $requestPath = $url;
  55422. }
  55423. if ($lastmodified === false || $lastmodified) {
  55424. $request = "GET $requestPath HTTP/1.1\r\n";
  55425. } else {
  55426. $request = "GET $requestPath HTTP/1.0\r\n";
  55427. }
  55428. $request .= "Host: $host\r\n";
  55429. $ifmodifiedsince = '';
  55430. if (is_array($lastmodified)) {
  55431. if (isset($lastmodified['Last-Modified'])) {
  55432. $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n";
  55433. }
  55434. if (isset($lastmodified['ETag'])) {
  55435. $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";
  55436. }
  55437. } else {
  55438. $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');
  55439. }
  55440. $request .= $ifmodifiedsince .
  55441. "User-Agent: PEAR/1.10.16/PHP/" . PHP_VERSION . "\r\n";
  55442. if ($object !== null) { // only pass in authentication for non-static calls
  55443. $username = $config->get('username', null, $channel);
  55444. $password = $config->get('password', null, $channel);
  55445. if ($username && $password) {
  55446. $tmp = base64_encode("$username:$password");
  55447. $request .= "Authorization: Basic $tmp\r\n";
  55448. }
  55449. }
  55450. $proxyAuth = $proxy->getProxyAuth();
  55451. if ($proxyAuth) {
  55452. $request .= 'Proxy-Authorization: Basic ' .
  55453. $proxyAuth . "\r\n";
  55454. }
  55455. if ($accept) {
  55456. $request .= 'Accept: ' . implode(', ', $accept) . "\r\n";
  55457. }
  55458. $request .= "Connection: close\r\n";
  55459. $request .= "\r\n";
  55460. fwrite($fp, $request);
  55461. $headers = array();
  55462. $reply = 0;
  55463. while (trim($line = fgets($fp, 1024))) {
  55464. if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) {
  55465. $headers[strtolower($matches[1])] = trim($matches[2]);
  55466. } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
  55467. $reply = (int)$matches[1];
  55468. if ($reply == 304 && ($lastmodified || ($lastmodified === false))) {
  55469. return false;
  55470. }
  55471. if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) {
  55472. return PEAR::raiseError("File $scheme://$host:$port$path not valid (received: $line)");
  55473. }
  55474. }
  55475. }
  55476. if ($reply != 200) {
  55477. if (!isset($headers['location'])) {
  55478. return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirected but no location)");
  55479. }
  55480. if ($wasredirect > 4) {
  55481. return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirection looped more than 5 times)");
  55482. }
  55483. $redirect = $wasredirect + 1;
  55484. return static::_downloadHttp($object, $headers['location'],
  55485. $ui, $save_dir, $callback, $lastmodified, $accept);
  55486. }
  55487. if (isset($headers['content-disposition']) &&
  55488. preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|\\z)/', $headers['content-disposition'], $matches)) {
  55489. $save_as = basename($matches[1]);
  55490. } else {
  55491. $save_as = basename($url);
  55492. }
  55493. if ($callback) {
  55494. $tmp = call_user_func($callback, 'saveas', $save_as);
  55495. if ($tmp) {
  55496. $save_as = $tmp;
  55497. }
  55498. }
  55499. $dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as;
  55500. if (is_link($dest_file)) {
  55501. return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $dest_file . ' as it is symlinked to ' . readlink($dest_file) . ' - Possible symlink attack');
  55502. }
  55503. if (!$wp = @fopen($dest_file, 'wb')) {
  55504. fclose($fp);
  55505. if ($callback) {
  55506. call_user_func($callback, 'writefailed',
  55507. array($dest_file, error_get_last()["message"]));
  55508. }
  55509. return PEAR::raiseError("could not open $dest_file for writing");
  55510. }
  55511. $length = isset($headers['content-length']) ? $headers['content-length'] : -1;
  55512. $bytes = 0;
  55513. if ($callback) {
  55514. call_user_func($callback, 'start', array(basename($dest_file), $length));
  55515. }
  55516. while ($data = fread($fp, 1024)) {
  55517. $bytes += strlen($data);
  55518. if ($callback) {
  55519. call_user_func($callback, 'bytesread', $bytes);
  55520. }
  55521. if (!@fwrite($wp, $data)) {
  55522. fclose($fp);
  55523. if ($callback) {
  55524. call_user_func($callback, 'writefailed',
  55525. array($dest_file, error_get_last()["message"]));
  55526. }
  55527. return PEAR::raiseError(
  55528. "$dest_file: write failed (" . error_get_last()["message"] . ")");
  55529. }
  55530. }
  55531. fclose($fp);
  55532. fclose($wp);
  55533. if ($callback) {
  55534. call_user_func($callback, 'done', $bytes);
  55535. }
  55536. if ($lastmodified === false || $lastmodified) {
  55537. if (isset($headers['etag'])) {
  55538. $lastmodified = array('ETag' => $headers['etag']);
  55539. }
  55540. if (isset($headers['last-modified'])) {
  55541. if (is_array($lastmodified)) {
  55542. $lastmodified['Last-Modified'] = $headers['last-modified'];
  55543. } else {
  55544. $lastmodified = $headers['last-modified'];
  55545. }
  55546. }
  55547. return array($dest_file, $lastmodified, $headers);
  55548. }
  55549. return $dest_file;
  55550. }
  55551. }
  55552. ���������������������������������������PEAR-1.10.16/PEAR/ErrorStack.php��������������������������������������������������������������������0000664�0001750�0001750�00000102014�14720722517�015605� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  55553. /**
  55554. * Error Stack Implementation
  55555. *
  55556. * This is an incredibly simple implementation of a very complex error handling
  55557. * facility. It contains the ability
  55558. * to track multiple errors from multiple packages simultaneously. In addition,
  55559. * it can track errors of many levels, save data along with the error, context
  55560. * information such as the exact file, line number, class and function that
  55561. * generated the error, and if necessary, it can raise a traditional PEAR_Error.
  55562. * It has built-in support for PEAR::Log, to log errors as they occur
  55563. *
  55564. * Since version 0.2alpha, it is also possible to selectively ignore errors,
  55565. * through the use of an error callback, see {@link pushCallback()}
  55566. *
  55567. * Since version 0.3alpha, it is possible to specify the exception class
  55568. * returned from {@link push()}
  55569. *
  55570. * Since version PEAR1.3.2, ErrorStack no longer instantiates an exception class. This can
  55571. * still be done quite handily in an error callback or by manipulating the returned array
  55572. * @category Debugging
  55573. * @package PEAR_ErrorStack
  55574. * @author Greg Beaver <cellog@php.net>
  55575. * @copyright 2004-2008 Greg Beaver
  55576. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  55577. * @link http://pear.php.net/package/PEAR_ErrorStack
  55578. */
  55579. /**
  55580. * Singleton storage
  55581. *
  55582. * Format:
  55583. * <pre>
  55584. * array(
  55585. * 'package1' => PEAR_ErrorStack object,
  55586. * 'package2' => PEAR_ErrorStack object,
  55587. * ...
  55588. * )
  55589. * </pre>
  55590. * @access private
  55591. * @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON']
  55592. */
  55593. $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array();
  55594. /**
  55595. * Global error callback (default)
  55596. *
  55597. * This is only used if set to non-false. * is the default callback for
  55598. * all packages, whereas specific packages may set a default callback
  55599. * for all instances, regardless of whether they are a singleton or not.
  55600. *
  55601. * To exclude non-singletons, only set the local callback for the singleton
  55602. * @see PEAR_ErrorStack::setDefaultCallback()
  55603. * @access private
  55604. * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']
  55605. */
  55606. $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array(
  55607. '*' => false,
  55608. );
  55609. /**
  55610. * Global Log object (default)
  55611. *
  55612. * This is only used if set to non-false. Use to set a default log object for
  55613. * all stacks, regardless of instantiation order or location
  55614. * @see PEAR_ErrorStack::setDefaultLogger()
  55615. * @access private
  55616. * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
  55617. */
  55618. $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false;
  55619. /**
  55620. * Global Overriding Callback
  55621. *
  55622. * This callback will override any error callbacks that specific loggers have set.
  55623. * Use with EXTREME caution
  55624. * @see PEAR_ErrorStack::staticPushCallback()
  55625. * @access private
  55626. * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
  55627. */
  55628. $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
  55629. /**#@+
  55630. * One of four possible return values from the error Callback
  55631. * @see PEAR_ErrorStack::_errorCallback()
  55632. */
  55633. /**
  55634. * If this is returned, then the error will be both pushed onto the stack
  55635. * and logged.
  55636. */
  55637. define('PEAR_ERRORSTACK_PUSHANDLOG', 1);
  55638. /**
  55639. * If this is returned, then the error will only be pushed onto the stack,
  55640. * and not logged.
  55641. */
  55642. define('PEAR_ERRORSTACK_PUSH', 2);
  55643. /**
  55644. * If this is returned, then the error will only be logged, but not pushed
  55645. * onto the error stack.
  55646. */
  55647. define('PEAR_ERRORSTACK_LOG', 3);
  55648. /**
  55649. * If this is returned, then the error is completely ignored.
  55650. */
  55651. define('PEAR_ERRORSTACK_IGNORE', 4);
  55652. /**
  55653. * If this is returned, then the error is logged and die() is called.
  55654. */
  55655. define('PEAR_ERRORSTACK_DIE', 5);
  55656. /**#@-*/
  55657. /**
  55658. * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in
  55659. * the singleton method.
  55660. */
  55661. define('PEAR_ERRORSTACK_ERR_NONCLASS', 1);
  55662. /**
  55663. * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()}
  55664. * that has no __toString() method
  55665. */
  55666. define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2);
  55667. /**
  55668. * Error Stack Implementation
  55669. *
  55670. * Usage:
  55671. * <code>
  55672. * // global error stack
  55673. * $global_stack = &PEAR_ErrorStack::singleton('MyPackage');
  55674. * // local error stack
  55675. * $local_stack = new PEAR_ErrorStack('MyPackage');
  55676. * </code>
  55677. * @author Greg Beaver <cellog@php.net>
  55678. * @version 1.10.16
  55679. * @package PEAR_ErrorStack
  55680. * @category Debugging
  55681. * @copyright 2004-2008 Greg Beaver
  55682. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  55683. * @link http://pear.php.net/package/PEAR_ErrorStack
  55684. */
  55685. class PEAR_ErrorStack {
  55686. /**
  55687. * Errors are stored in the order that they are pushed on the stack.
  55688. * @since 0.4alpha Errors are no longer organized by error level.
  55689. * This renders pop() nearly unusable, and levels could be more easily
  55690. * handled in a callback anyway
  55691. * @var array
  55692. * @access private
  55693. */
  55694. var $_errors = array();
  55695. /**
  55696. * Storage of errors by level.
  55697. *
  55698. * Allows easy retrieval and deletion of only errors from a particular level
  55699. * @since PEAR 1.4.0dev
  55700. * @var array
  55701. * @access private
  55702. */
  55703. var $_errorsByLevel = array();
  55704. /**
  55705. * Package name this error stack represents
  55706. * @var string
  55707. * @access protected
  55708. */
  55709. var $_package;
  55710. /**
  55711. * Determines whether a PEAR_Error is thrown upon every error addition
  55712. * @var boolean
  55713. * @access private
  55714. */
  55715. var $_compat = false;
  55716. /**
  55717. * If set to a valid callback, this will be used to generate the error
  55718. * message from the error code, otherwise the message passed in will be
  55719. * used
  55720. * @var false|string|array
  55721. * @access private
  55722. */
  55723. var $_msgCallback = false;
  55724. /**
  55725. * If set to a valid callback, this will be used to generate the error
  55726. * context for an error. For PHP-related errors, this will be a file
  55727. * and line number as retrieved from debug_backtrace(), but can be
  55728. * customized for other purposes. The error might actually be in a separate
  55729. * configuration file, or in a database query.
  55730. * @var false|string|array
  55731. * @access protected
  55732. */
  55733. var $_contextCallback = false;
  55734. /**
  55735. * If set to a valid callback, this will be called every time an error
  55736. * is pushed onto the stack. The return value will be used to determine
  55737. * whether to allow an error to be pushed or logged.
  55738. *
  55739. * The return value must be one an PEAR_ERRORSTACK_* constant
  55740. * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
  55741. * @var false|string|array
  55742. * @access protected
  55743. */
  55744. var $_errorCallback = array();
  55745. /**
  55746. * PEAR::Log object for logging errors
  55747. * @var false|Log
  55748. * @access protected
  55749. */
  55750. var $_logger = false;
  55751. /**
  55752. * Error messages - designed to be overridden
  55753. * @var array
  55754. * @abstract
  55755. */
  55756. var $_errorMsgs = array();
  55757. /**
  55758. * Set up a new error stack
  55759. *
  55760. * @param string $package name of the package this error stack represents
  55761. * @param callback $msgCallback callback used for error message generation
  55762. * @param callback $contextCallback callback used for context generation,
  55763. * defaults to {@link getFileLine()}
  55764. * @param boolean $throwPEAR_Error
  55765. */
  55766. function __construct($package, $msgCallback = false, $contextCallback = false,
  55767. $throwPEAR_Error = false)
  55768. {
  55769. $this->_package = $package;
  55770. $this->setMessageCallback($msgCallback);
  55771. $this->setContextCallback($contextCallback);
  55772. $this->_compat = $throwPEAR_Error;
  55773. }
  55774. /**
  55775. * Return a single error stack for this package.
  55776. *
  55777. * Note that all parameters are ignored if the stack for package $package
  55778. * has already been instantiated
  55779. * @param string $package name of the package this error stack represents
  55780. * @param callback $msgCallback callback used for error message generation
  55781. * @param callback $contextCallback callback used for context generation,
  55782. * defaults to {@link getFileLine()}
  55783. * @param boolean $throwPEAR_Error
  55784. * @param string $stackClass class to instantiate
  55785. *
  55786. * @return PEAR_ErrorStack
  55787. */
  55788. public static function &singleton(
  55789. $package, $msgCallback = false, $contextCallback = false,
  55790. $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack'
  55791. ) {
  55792. if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
  55793. return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
  55794. }
  55795. if (!class_exists($stackClass)) {
  55796. if (function_exists('debug_backtrace')) {
  55797. $trace = debug_backtrace();
  55798. }
  55799. PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS,
  55800. 'exception', array('stackclass' => $stackClass),
  55801. 'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)',
  55802. false, $trace);
  55803. }
  55804. $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] =
  55805. new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error);
  55806. return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
  55807. }
  55808. /**
  55809. * Internal error handler for PEAR_ErrorStack class
  55810. *
  55811. * Dies if the error is an exception (and would have died anyway)
  55812. * @access private
  55813. */
  55814. function _handleError($err)
  55815. {
  55816. if ($err['level'] == 'exception') {
  55817. $message = $err['message'];
  55818. if (isset($_SERVER['REQUEST_URI'])) {
  55819. echo '<br />';
  55820. } else {
  55821. echo "\n";
  55822. }
  55823. var_dump($err['context']);
  55824. die($message);
  55825. }
  55826. }
  55827. /**
  55828. * Set up a PEAR::Log object for all error stacks that don't have one
  55829. * @param Log $log
  55830. */
  55831. public static function setDefaultLogger(&$log)
  55832. {
  55833. if (is_object($log) && method_exists($log, 'log') ) {
  55834. $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
  55835. } elseif (is_callable($log)) {
  55836. $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
  55837. }
  55838. }
  55839. /**
  55840. * Set up a PEAR::Log object for this error stack
  55841. * @param Log $log
  55842. */
  55843. function setLogger(&$log)
  55844. {
  55845. if (is_object($log) && method_exists($log, 'log') ) {
  55846. $this->_logger = &$log;
  55847. } elseif (is_callable($log)) {
  55848. $this->_logger = &$log;
  55849. }
  55850. }
  55851. /**
  55852. * Set an error code => error message mapping callback
  55853. *
  55854. * This method sets the callback that can be used to generate error
  55855. * messages for any instance
  55856. * @param array|string Callback function/method
  55857. */
  55858. function setMessageCallback($msgCallback)
  55859. {
  55860. if (!$msgCallback) {
  55861. $this->_msgCallback = array(&$this, 'getErrorMessage');
  55862. } else {
  55863. if (is_callable($msgCallback)) {
  55864. $this->_msgCallback = $msgCallback;
  55865. }
  55866. }
  55867. }
  55868. /**
  55869. * Get an error code => error message mapping callback
  55870. *
  55871. * This method returns the current callback that can be used to generate error
  55872. * messages
  55873. * @return array|string|false Callback function/method or false if none
  55874. */
  55875. function getMessageCallback()
  55876. {
  55877. return $this->_msgCallback;
  55878. }
  55879. /**
  55880. * Sets a default callback to be used by all error stacks
  55881. *
  55882. * This method sets the callback that can be used to generate error
  55883. * messages for a singleton
  55884. * @param array|string Callback function/method
  55885. * @param string Package name, or false for all packages
  55886. */
  55887. public static function setDefaultCallback($callback = false, $package = false)
  55888. {
  55889. if (!is_callable($callback)) {
  55890. $callback = false;
  55891. }
  55892. $package = $package ? $package : '*';
  55893. $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback;
  55894. }
  55895. /**
  55896. * Set a callback that generates context information (location of error) for an error stack
  55897. *
  55898. * This method sets the callback that can be used to generate context
  55899. * information for an error. Passing in NULL will disable context generation
  55900. * and remove the expensive call to debug_backtrace()
  55901. * @param array|string|null Callback function/method
  55902. */
  55903. function setContextCallback($contextCallback)
  55904. {
  55905. if ($contextCallback === null) {
  55906. return $this->_contextCallback = false;
  55907. }
  55908. if (!$contextCallback) {
  55909. $this->_contextCallback = array(&$this, 'getFileLine');
  55910. } else {
  55911. if (is_callable($contextCallback)) {
  55912. $this->_contextCallback = $contextCallback;
  55913. }
  55914. }
  55915. }
  55916. /**
  55917. * Set an error Callback
  55918. * If set to a valid callback, this will be called every time an error
  55919. * is pushed onto the stack. The return value will be used to determine
  55920. * whether to allow an error to be pushed or logged.
  55921. *
  55922. * The return value must be one of the ERRORSTACK_* constants.
  55923. *
  55924. * This functionality can be used to emulate PEAR's pushErrorHandling, and
  55925. * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of
  55926. * the error stack or logging
  55927. * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
  55928. * @see popCallback()
  55929. * @param string|array $cb
  55930. */
  55931. function pushCallback($cb)
  55932. {
  55933. array_push($this->_errorCallback, $cb);
  55934. }
  55935. /**
  55936. * Remove a callback from the error callback stack
  55937. * @see pushCallback()
  55938. * @return array|string|false
  55939. */
  55940. function popCallback()
  55941. {
  55942. if (!count($this->_errorCallback)) {
  55943. return false;
  55944. }
  55945. return array_pop($this->_errorCallback);
  55946. }
  55947. /**
  55948. * Set a temporary overriding error callback for every package error stack
  55949. *
  55950. * Use this to temporarily disable all existing callbacks (can be used
  55951. * to emulate the @ operator, for instance)
  55952. * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
  55953. * @see staticPopCallback(), pushCallback()
  55954. * @param string|array $cb
  55955. */
  55956. public static function staticPushCallback($cb)
  55957. {
  55958. array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb);
  55959. }
  55960. /**
  55961. * Remove a temporary overriding error callback
  55962. * @see staticPushCallback()
  55963. * @return array|string|false
  55964. */
  55965. public static function staticPopCallback()
  55966. {
  55967. $ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']);
  55968. if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) {
  55969. $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
  55970. }
  55971. return $ret;
  55972. }
  55973. /**
  55974. * Add an error to the stack
  55975. *
  55976. * If the message generator exists, it is called with 2 parameters.
  55977. * - the current Error Stack object
  55978. * - an array that is in the same format as an error. Available indices
  55979. * are 'code', 'package', 'time', 'params', 'level', and 'context'
  55980. *
  55981. * Next, if the error should contain context information, this is
  55982. * handled by the context grabbing method.
  55983. * Finally, the error is pushed onto the proper error stack
  55984. * @param int $code Package-specific error code
  55985. * @param string $level Error level. This is NOT spell-checked
  55986. * @param array $params associative array of error parameters
  55987. * @param string $msg Error message, or a portion of it if the message
  55988. * is to be generated
  55989. * @param array $repackage If this error re-packages an error pushed by
  55990. * another package, place the array returned from
  55991. * {@link pop()} in this parameter
  55992. * @param array $backtrace Protected parameter: use this to pass in the
  55993. * {@link debug_backtrace()} that should be used
  55994. * to find error context
  55995. * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also
  55996. * thrown. If a PEAR_Error is returned, the userinfo
  55997. * property is set to the following array:
  55998. *
  55999. * <code>
  56000. * array(
  56001. * 'code' => $code,
  56002. * 'params' => $params,
  56003. * 'package' => $this->_package,
  56004. * 'level' => $level,
  56005. * 'time' => time(),
  56006. * 'context' => $context,
  56007. * 'message' => $msg,
  56008. * //['repackage' => $err] repackaged error array/Exception class
  56009. * );
  56010. * </code>
  56011. *
  56012. * Normally, the previous array is returned.
  56013. */
  56014. function push($code, $level = 'error', $params = array(), $msg = false,
  56015. $repackage = false, $backtrace = false)
  56016. {
  56017. $context = false;
  56018. // grab error context
  56019. if ($this->_contextCallback) {
  56020. if (!$backtrace) {
  56021. $backtrace = debug_backtrace();
  56022. }
  56023. $context = call_user_func($this->_contextCallback, $code, $params, $backtrace);
  56024. }
  56025. // save error
  56026. $time = explode(' ', microtime());
  56027. $time = $time[1] + $time[0];
  56028. $err = array(
  56029. 'code' => $code,
  56030. 'params' => $params,
  56031. 'package' => $this->_package,
  56032. 'level' => $level,
  56033. 'time' => $time,
  56034. 'context' => $context,
  56035. 'message' => $msg,
  56036. );
  56037. if ($repackage) {
  56038. $err['repackage'] = $repackage;
  56039. }
  56040. // set up the error message, if necessary
  56041. if ($this->_msgCallback) {
  56042. $msg = call_user_func_array($this->_msgCallback,
  56043. array(&$this, $err));
  56044. $err['message'] = $msg;
  56045. }
  56046. $push = $log = true;
  56047. $die = false;
  56048. // try the overriding callback first
  56049. $callback = $this->staticPopCallback();
  56050. if ($callback) {
  56051. $this->staticPushCallback($callback);
  56052. }
  56053. if (!is_callable($callback)) {
  56054. // try the local callback next
  56055. $callback = $this->popCallback();
  56056. if (is_callable($callback)) {
  56057. $this->pushCallback($callback);
  56058. } else {
  56059. // try the default callback
  56060. $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ?
  56061. $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] :
  56062. $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*'];
  56063. }
  56064. }
  56065. if (is_callable($callback)) {
  56066. switch(call_user_func($callback, $err)){
  56067. case PEAR_ERRORSTACK_IGNORE:
  56068. return $err;
  56069. break;
  56070. case PEAR_ERRORSTACK_PUSH:
  56071. $log = false;
  56072. break;
  56073. case PEAR_ERRORSTACK_LOG:
  56074. $push = false;
  56075. break;
  56076. case PEAR_ERRORSTACK_DIE:
  56077. $die = true;
  56078. break;
  56079. // anything else returned has the same effect as pushandlog
  56080. }
  56081. }
  56082. if ($push) {
  56083. array_unshift($this->_errors, $err);
  56084. if (!isset($this->_errorsByLevel[$err['level']])) {
  56085. $this->_errorsByLevel[$err['level']] = array();
  56086. }
  56087. $this->_errorsByLevel[$err['level']][] = &$this->_errors[0];
  56088. }
  56089. if ($log) {
  56090. if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) {
  56091. $this->_log($err);
  56092. }
  56093. }
  56094. if ($die) {
  56095. die();
  56096. }
  56097. if ($this->_compat && $push) {
  56098. return $this->raiseError($msg, $code, null, null, $err);
  56099. }
  56100. return $err;
  56101. }
  56102. /**
  56103. * Static version of {@link push()}
  56104. *
  56105. * @param string $package Package name this error belongs to
  56106. * @param int $code Package-specific error code
  56107. * @param string $level Error level. This is NOT spell-checked
  56108. * @param array $params associative array of error parameters
  56109. * @param string $msg Error message, or a portion of it if the message
  56110. * is to be generated
  56111. * @param array $repackage If this error re-packages an error pushed by
  56112. * another package, place the array returned from
  56113. * {@link pop()} in this parameter
  56114. * @param array $backtrace Protected parameter: use this to pass in the
  56115. * {@link debug_backtrace()} that should be used
  56116. * to find error context
  56117. * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also
  56118. * thrown. see docs for {@link push()}
  56119. */
  56120. public static function staticPush(
  56121. $package, $code, $level = 'error', $params = array(),
  56122. $msg = false, $repackage = false, $backtrace = false
  56123. ) {
  56124. $s = &PEAR_ErrorStack::singleton($package);
  56125. if ($s->_contextCallback) {
  56126. if (!$backtrace) {
  56127. if (function_exists('debug_backtrace')) {
  56128. $backtrace = debug_backtrace();
  56129. }
  56130. }
  56131. }
  56132. return $s->push($code, $level, $params, $msg, $repackage, $backtrace);
  56133. }
  56134. /**
  56135. * Log an error using PEAR::Log
  56136. * @param array $err Error array
  56137. * @param array $levels Error level => Log constant map
  56138. * @access protected
  56139. */
  56140. function _log($err)
  56141. {
  56142. if ($this->_logger) {
  56143. $logger = &$this->_logger;
  56144. } else {
  56145. $logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'];
  56146. }
  56147. if (is_a($logger, 'Log')) {
  56148. $levels = array(
  56149. 'exception' => PEAR_LOG_CRIT,
  56150. 'alert' => PEAR_LOG_ALERT,
  56151. 'critical' => PEAR_LOG_CRIT,
  56152. 'error' => PEAR_LOG_ERR,
  56153. 'warning' => PEAR_LOG_WARNING,
  56154. 'notice' => PEAR_LOG_NOTICE,
  56155. 'info' => PEAR_LOG_INFO,
  56156. 'debug' => PEAR_LOG_DEBUG);
  56157. if (isset($levels[$err['level']])) {
  56158. $level = $levels[$err['level']];
  56159. } else {
  56160. $level = PEAR_LOG_INFO;
  56161. }
  56162. $logger->log($err['message'], $level, $err);
  56163. } else { // support non-standard logs
  56164. call_user_func($logger, $err);
  56165. }
  56166. }
  56167. /**
  56168. * Pop an error off of the error stack
  56169. *
  56170. * @return false|array
  56171. * @since 0.4alpha it is no longer possible to specify a specific error
  56172. * level to return - the last error pushed will be returned, instead
  56173. */
  56174. function pop()
  56175. {
  56176. $err = @array_shift($this->_errors);
  56177. if (!is_null($err)) {
  56178. @array_pop($this->_errorsByLevel[$err['level']]);
  56179. if (!count($this->_errorsByLevel[$err['level']])) {
  56180. unset($this->_errorsByLevel[$err['level']]);
  56181. }
  56182. }
  56183. return $err;
  56184. }
  56185. /**
  56186. * Pop an error off of the error stack, static method
  56187. *
  56188. * @param string package name
  56189. * @return boolean
  56190. * @since PEAR1.5.0a1
  56191. */
  56192. static function staticPop($package)
  56193. {
  56194. if ($package) {
  56195. if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
  56196. return false;
  56197. }
  56198. return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->pop();
  56199. }
  56200. }
  56201. /**
  56202. * Determine whether there are any errors on the stack
  56203. * @param string|array Level name. Use to determine if any errors
  56204. * of level (string), or levels (array) have been pushed
  56205. * @return boolean
  56206. */
  56207. function hasErrors($level = false)
  56208. {
  56209. if ($level) {
  56210. return isset($this->_errorsByLevel[$level]);
  56211. }
  56212. return count($this->_errors);
  56213. }
  56214. /**
  56215. * Retrieve all errors since last purge
  56216. *
  56217. * @param boolean set in order to empty the error stack
  56218. * @param string level name, to return only errors of a particular severity
  56219. * @return array
  56220. */
  56221. function getErrors($purge = false, $level = false)
  56222. {
  56223. if (!$purge) {
  56224. if ($level) {
  56225. if (!isset($this->_errorsByLevel[$level])) {
  56226. return array();
  56227. } else {
  56228. return $this->_errorsByLevel[$level];
  56229. }
  56230. } else {
  56231. return $this->_errors;
  56232. }
  56233. }
  56234. if ($level) {
  56235. $ret = $this->_errorsByLevel[$level];
  56236. foreach ($this->_errorsByLevel[$level] as $i => $unused) {
  56237. // entries are references to the $_errors array
  56238. $this->_errorsByLevel[$level][$i] = false;
  56239. }
  56240. // array_filter removes all entries === false
  56241. $this->_errors = array_filter($this->_errors);
  56242. unset($this->_errorsByLevel[$level]);
  56243. return $ret;
  56244. }
  56245. $ret = $this->_errors;
  56246. $this->_errors = array();
  56247. $this->_errorsByLevel = array();
  56248. return $ret;
  56249. }
  56250. /**
  56251. * Determine whether there are any errors on a single error stack, or on any error stack
  56252. *
  56253. * The optional parameter can be used to test the existence of any errors without the need of
  56254. * singleton instantiation
  56255. * @param string|false Package name to check for errors
  56256. * @param string Level name to check for a particular severity
  56257. * @return boolean
  56258. */
  56259. public static function staticHasErrors($package = false, $level = false)
  56260. {
  56261. if ($package) {
  56262. if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
  56263. return false;
  56264. }
  56265. return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level);
  56266. }
  56267. foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
  56268. if ($obj->hasErrors($level)) {
  56269. return true;
  56270. }
  56271. }
  56272. return false;
  56273. }
  56274. /**
  56275. * Get a list of all errors since last purge, organized by package
  56276. * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be
  56277. * @param boolean $purge Set to purge the error stack of existing errors
  56278. * @param string $level Set to a level name in order to retrieve only errors of a particular level
  56279. * @param boolean $merge Set to return a flat array, not organized by package
  56280. * @param array $sortfunc Function used to sort a merged array - default
  56281. * sorts by time, and should be good for most cases
  56282. *
  56283. * @return array
  56284. */
  56285. public static function staticGetErrors(
  56286. $purge = false, $level = false, $merge = false,
  56287. $sortfunc = array('PEAR_ErrorStack', '_sortErrors')
  56288. ) {
  56289. $ret = array();
  56290. if (!is_callable($sortfunc)) {
  56291. $sortfunc = array('PEAR_ErrorStack', '_sortErrors');
  56292. }
  56293. foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
  56294. $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level);
  56295. if ($test) {
  56296. if ($merge) {
  56297. $ret = array_merge($ret, $test);
  56298. } else {
  56299. $ret[$package] = $test;
  56300. }
  56301. }
  56302. }
  56303. if ($merge) {
  56304. usort($ret, $sortfunc);
  56305. }
  56306. return $ret;
  56307. }
  56308. /**
  56309. * Error sorting function, sorts by time
  56310. * @access private
  56311. */
  56312. public static function _sortErrors($a, $b)
  56313. {
  56314. if ($a['time'] == $b['time']) {
  56315. return 0;
  56316. }
  56317. if ($a['time'] < $b['time']) {
  56318. return 1;
  56319. }
  56320. return -1;
  56321. }
  56322. /**
  56323. * Standard file/line number/function/class context callback
  56324. *
  56325. * This function uses a backtrace generated from {@link debug_backtrace()}
  56326. * and so will not work at all in PHP < 4.3.0. The frame should
  56327. * reference the frame that contains the source of the error.
  56328. * @return array|false either array('file' => file, 'line' => line,
  56329. * 'function' => function name, 'class' => class name) or
  56330. * if this doesn't work, then false
  56331. * @param unused
  56332. * @param integer backtrace frame.
  56333. * @param array Results of debug_backtrace()
  56334. */
  56335. public static function getFileLine($code, $params, $backtrace = null)
  56336. {
  56337. if ($backtrace === null) {
  56338. return false;
  56339. }
  56340. $frame = 0;
  56341. $functionframe = 1;
  56342. if (!isset($backtrace[1])) {
  56343. $functionframe = 0;
  56344. } else {
  56345. while (isset($backtrace[$functionframe]['function']) &&
  56346. $backtrace[$functionframe]['function'] == 'eval' &&
  56347. isset($backtrace[$functionframe + 1])) {
  56348. $functionframe++;
  56349. }
  56350. }
  56351. if (isset($backtrace[$frame])) {
  56352. if (!isset($backtrace[$frame]['file'])) {
  56353. $frame++;
  56354. }
  56355. $funcbacktrace = $backtrace[$functionframe];
  56356. $filebacktrace = $backtrace[$frame];
  56357. $ret = array('file' => $filebacktrace['file'],
  56358. 'line' => $filebacktrace['line']);
  56359. // rearrange for eval'd code or create function errors
  56360. if (strpos($filebacktrace['file'], '(') &&
  56361. preg_match(';^(.*?)\((\d+)\) : (.*?)\\z;', $filebacktrace['file'],
  56362. $matches)) {
  56363. $ret['file'] = $matches[1];
  56364. $ret['line'] = $matches[2] + 0;
  56365. }
  56366. if (isset($funcbacktrace['function']) && isset($backtrace[1])) {
  56367. if ($funcbacktrace['function'] != 'eval') {
  56368. if ($funcbacktrace['function'] == '__lambda_func') {
  56369. $ret['function'] = 'create_function() code';
  56370. } else {
  56371. $ret['function'] = $funcbacktrace['function'];
  56372. }
  56373. }
  56374. }
  56375. if (isset($funcbacktrace['class']) && isset($backtrace[1])) {
  56376. $ret['class'] = $funcbacktrace['class'];
  56377. }
  56378. return $ret;
  56379. }
  56380. return false;
  56381. }
  56382. /**
  56383. * Standard error message generation callback
  56384. *
  56385. * This method may also be called by a custom error message generator
  56386. * to fill in template values from the params array, simply
  56387. * set the third parameter to the error message template string to use
  56388. *
  56389. * The special variable %__msg% is reserved: use it only to specify
  56390. * where a message passed in by the user should be placed in the template,
  56391. * like so:
  56392. *
  56393. * Error message: %msg% - internal error
  56394. *
  56395. * If the message passed like so:
  56396. *
  56397. * <code>
  56398. * $stack->push(ERROR_CODE, 'error', array(), 'server error 500');
  56399. * </code>
  56400. *
  56401. * The returned error message will be "Error message: server error 500 -
  56402. * internal error"
  56403. * @param PEAR_ErrorStack
  56404. * @param array
  56405. * @param string|false Pre-generated error message template
  56406. *
  56407. * @return string
  56408. */
  56409. public static function getErrorMessage(&$stack, $err, $template = false)
  56410. {
  56411. if ($template) {
  56412. $mainmsg = $template;
  56413. } else {
  56414. $mainmsg = $stack->getErrorMessageTemplate($err['code']);
  56415. }
  56416. $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg);
  56417. if (is_array($err['params']) && count($err['params'])) {
  56418. foreach ($err['params'] as $name => $val) {
  56419. if (is_array($val)) {
  56420. // @ is needed in case $val is a multi-dimensional array
  56421. $val = @implode(', ', $val);
  56422. }
  56423. if (is_object($val)) {
  56424. if (method_exists($val, '__toString')) {
  56425. $val = $val->__toString();
  56426. } else {
  56427. PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING,
  56428. 'warning', array('obj' => get_class($val)),
  56429. 'object %obj% passed into getErrorMessage, but has no __toString() method');
  56430. $val = 'Object';
  56431. }
  56432. }
  56433. $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg);
  56434. }
  56435. }
  56436. return $mainmsg;
  56437. }
  56438. /**
  56439. * Standard Error Message Template generator from code
  56440. * @return string
  56441. */
  56442. function getErrorMessageTemplate($code)
  56443. {
  56444. if (!isset($this->_errorMsgs[$code])) {
  56445. return '%__msg%';
  56446. }
  56447. return $this->_errorMsgs[$code];
  56448. }
  56449. /**
  56450. * Set the Error Message Template array
  56451. *
  56452. * The array format must be:
  56453. * <pre>
  56454. * array(error code => 'message template',...)
  56455. * </pre>
  56456. *
  56457. * Error message parameters passed into {@link push()} will be used as input
  56458. * for the error message. If the template is 'message %foo% was %bar%', and the
  56459. * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will
  56460. * be 'message one was six'
  56461. * @return string
  56462. */
  56463. function setErrorMessageTemplate($template)
  56464. {
  56465. $this->_errorMsgs = $template;
  56466. }
  56467. /**
  56468. * emulate PEAR::raiseError()
  56469. *
  56470. * @return PEAR_Error
  56471. */
  56472. function raiseError()
  56473. {
  56474. require_once 'PEAR.php';
  56475. $args = func_get_args();
  56476. return call_user_func_array(array('PEAR', 'raiseError'), $args);
  56477. }
  56478. }
  56479. $stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack');
  56480. $stack->pushCallback(array('PEAR_ErrorStack', '_handleError'));
  56481. ?>
  56482. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Exception.php���������������������������������������������������������������������0000644�0001750�0001750�00000033155�14720722517�015473� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  56483. /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
  56484. /**
  56485. * PEAR_Exception
  56486. *
  56487. * PHP versions 4 and 5
  56488. *
  56489. * @category pear
  56490. * @package PEAR
  56491. * @author Tomas V. V. Cox <cox@idecnet.com>
  56492. * @author Hans Lellelid <hans@velum.net>
  56493. * @author Bertrand Mansion <bmansion@mamasam.com>
  56494. * @author Greg Beaver <cellog@php.net>
  56495. * @copyright 1997-2009 The Authors
  56496. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  56497. * @link http://pear.php.net/package/PEAR
  56498. * @since File available since Release 1.3.3
  56499. */
  56500. /**
  56501. * Base PEAR_Exception Class
  56502. *
  56503. * 1) Features:
  56504. *
  56505. * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception))
  56506. * - Definable triggers, shot when exceptions occur
  56507. * - Pretty and informative error messages
  56508. * - Added more context info available (like class, method or cause)
  56509. * - cause can be a PEAR_Exception or an array of mixed
  56510. * PEAR_Exceptions/PEAR_ErrorStack warnings
  56511. * - callbacks for specific exception classes and their children
  56512. *
  56513. * 2) Ideas:
  56514. *
  56515. * - Maybe a way to define a 'template' for the output
  56516. *
  56517. * 3) Inherited properties from PHP Exception Class:
  56518. *
  56519. * protected $message
  56520. * protected $code
  56521. * protected $line
  56522. * protected $file
  56523. * private $trace
  56524. *
  56525. * 4) Inherited methods from PHP Exception Class:
  56526. *
  56527. * __clone
  56528. * __construct
  56529. * getMessage
  56530. * getCode
  56531. * getFile
  56532. * getLine
  56533. * getTraceSafe
  56534. * getTraceSafeAsString
  56535. * __toString
  56536. *
  56537. * 5) Usage example
  56538. *
  56539. * <code>
  56540. * require_once 'PEAR/Exception.php';
  56541. *
  56542. * class Test {
  56543. * function foo() {
  56544. * throw new PEAR_Exception('Error Message', ERROR_CODE);
  56545. * }
  56546. * }
  56547. *
  56548. * function myLogger($pear_exception) {
  56549. * echo $pear_exception->getMessage();
  56550. * }
  56551. * // each time a exception is thrown the 'myLogger' will be called
  56552. * // (its use is completely optional)
  56553. * PEAR_Exception::addObserver('myLogger');
  56554. * $test = new Test;
  56555. * try {
  56556. * $test->foo();
  56557. * } catch (PEAR_Exception $e) {
  56558. * print $e;
  56559. * }
  56560. * </code>
  56561. *
  56562. * @category pear
  56563. * @package PEAR
  56564. * @author Tomas V.V.Cox <cox@idecnet.com>
  56565. * @author Hans Lellelid <hans@velum.net>
  56566. * @author Bertrand Mansion <bmansion@mamasam.com>
  56567. * @author Greg Beaver <cellog@php.net>
  56568. * @copyright 1997-2009 The Authors
  56569. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  56570. * @version Release: 1.10.16
  56571. * @link http://pear.php.net/package/PEAR
  56572. * @since Class available since Release 1.3.3
  56573. *
  56574. */
  56575. class PEAR_Exception extends Exception
  56576. {
  56577. const OBSERVER_PRINT = -2;
  56578. const OBSERVER_TRIGGER = -4;
  56579. const OBSERVER_DIE = -8;
  56580. protected $cause;
  56581. private static $_observers = array();
  56582. private static $_uniqueid = 0;
  56583. private $_trace;
  56584. /**
  56585. * Supported signatures:
  56586. * - PEAR_Exception(string $message);
  56587. * - PEAR_Exception(string $message, int $code);
  56588. * - PEAR_Exception(string $message, Exception $cause);
  56589. * - PEAR_Exception(string $message, Exception $cause, int $code);
  56590. * - PEAR_Exception(string $message, PEAR_Error $cause);
  56591. * - PEAR_Exception(string $message, PEAR_Error $cause, int $code);
  56592. * - PEAR_Exception(string $message, array $causes);
  56593. * - PEAR_Exception(string $message, array $causes, int $code);
  56594. * @param string exception message
  56595. * @param int|Exception|PEAR_Error|array|null exception cause
  56596. * @param int|null exception code or null
  56597. */
  56598. public function __construct($message, $p2 = null, $p3 = null)
  56599. {
  56600. if (is_int($p2)) {
  56601. $code = $p2;
  56602. $this->cause = null;
  56603. } elseif (is_object($p2) || is_array($p2)) {
  56604. // using is_object allows both Exception and PEAR_Error
  56605. if (is_object($p2) && !($p2 instanceof Exception)) {
  56606. if (!class_exists('PEAR_Error') || !($p2 instanceof PEAR_Error)) {
  56607. throw new PEAR_Exception('exception cause must be Exception, ' .
  56608. 'array, or PEAR_Error');
  56609. }
  56610. }
  56611. $code = $p3;
  56612. if (is_array($p2) && isset($p2['message'])) {
  56613. // fix potential problem of passing in a single warning
  56614. $p2 = array($p2);
  56615. }
  56616. $this->cause = $p2;
  56617. } else {
  56618. $code = null;
  56619. $this->cause = null;
  56620. }
  56621. parent::__construct($message, $code);
  56622. $this->signal();
  56623. }
  56624. /**
  56625. * @param mixed $callback - A valid php callback, see php func is_callable()
  56626. * - A PEAR_Exception::OBSERVER_* constant
  56627. * - An array(const PEAR_Exception::OBSERVER_*,
  56628. * mixed $options)
  56629. * @param string $label The name of the observer. Use this if you want
  56630. * to remove it later with removeObserver()
  56631. */
  56632. public static function addObserver($callback, $label = 'default')
  56633. {
  56634. self::$_observers[$label] = $callback;
  56635. }
  56636. public static function removeObserver($label = 'default')
  56637. {
  56638. unset(self::$_observers[$label]);
  56639. }
  56640. /**
  56641. * @return int unique identifier for an observer
  56642. */
  56643. public static function getUniqueId()
  56644. {
  56645. return self::$_uniqueid++;
  56646. }
  56647. private function signal()
  56648. {
  56649. foreach (self::$_observers as $func) {
  56650. if (is_callable($func)) {
  56651. call_user_func($func, $this);
  56652. continue;
  56653. }
  56654. settype($func, 'array');
  56655. switch ($func[0]) {
  56656. case self::OBSERVER_PRINT :
  56657. $f = (isset($func[1])) ? $func[1] : '%s';
  56658. printf($f, $this->getMessage());
  56659. break;
  56660. case self::OBSERVER_TRIGGER :
  56661. $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE;
  56662. trigger_error($this->getMessage(), $f);
  56663. break;
  56664. case self::OBSERVER_DIE :
  56665. $f = (isset($func[1])) ? $func[1] : '%s';
  56666. die(printf($f, $this->getMessage()));
  56667. break;
  56668. default:
  56669. trigger_error('invalid observer type', E_USER_WARNING);
  56670. }
  56671. }
  56672. }
  56673. /**
  56674. * Return specific error information that can be used for more detailed
  56675. * error messages or translation.
  56676. *
  56677. * This method may be overridden in child exception classes in order
  56678. * to add functionality not present in PEAR_Exception and is a placeholder
  56679. * to define API
  56680. *
  56681. * The returned array must be an associative array of parameter => value like so:
  56682. * <pre>
  56683. * array('name' => $name, 'context' => array(...))
  56684. * </pre>
  56685. * @return array
  56686. */
  56687. public function getErrorData()
  56688. {
  56689. return array();
  56690. }
  56691. /**
  56692. * Returns the exception that caused this exception to be thrown
  56693. * @access public
  56694. * @return Exception|array The context of the exception
  56695. */
  56696. public function getCause()
  56697. {
  56698. return $this->cause;
  56699. }
  56700. /**
  56701. * Function must be public to call on caused exceptions
  56702. * @param array
  56703. */
  56704. public function getCauseMessage(&$causes)
  56705. {
  56706. $trace = $this->getTraceSafe();
  56707. $cause = array('class' => get_class($this),
  56708. 'message' => $this->message,
  56709. 'file' => 'unknown',
  56710. 'line' => 'unknown');
  56711. if (isset($trace[0])) {
  56712. if (isset($trace[0]['file'])) {
  56713. $cause['file'] = $trace[0]['file'];
  56714. $cause['line'] = $trace[0]['line'];
  56715. }
  56716. }
  56717. $causes[] = $cause;
  56718. if ($this->cause instanceof PEAR_Exception) {
  56719. $this->cause->getCauseMessage($causes);
  56720. } elseif ($this->cause instanceof Exception) {
  56721. $causes[] = array('class' => get_class($this->cause),
  56722. 'message' => $this->cause->getMessage(),
  56723. 'file' => $this->cause->getFile(),
  56724. 'line' => $this->cause->getLine());
  56725. } elseif (class_exists('PEAR_Error') && $this->cause instanceof PEAR_Error) {
  56726. $causes[] = array('class' => get_class($this->cause),
  56727. 'message' => $this->cause->getMessage(),
  56728. 'file' => 'unknown',
  56729. 'line' => 'unknown');
  56730. } elseif (is_array($this->cause)) {
  56731. foreach ($this->cause as $cause) {
  56732. if ($cause instanceof PEAR_Exception) {
  56733. $cause->getCauseMessage($causes);
  56734. } elseif ($cause instanceof Exception) {
  56735. $causes[] = array('class' => get_class($cause),
  56736. 'message' => $cause->getMessage(),
  56737. 'file' => $cause->getFile(),
  56738. 'line' => $cause->getLine());
  56739. } elseif (class_exists('PEAR_Error') && $cause instanceof PEAR_Error) {
  56740. $causes[] = array('class' => get_class($cause),
  56741. 'message' => $cause->getMessage(),
  56742. 'file' => 'unknown',
  56743. 'line' => 'unknown');
  56744. } elseif (is_array($cause) && isset($cause['message'])) {
  56745. // PEAR_ErrorStack warning
  56746. $causes[] = array(
  56747. 'class' => $cause['package'],
  56748. 'message' => $cause['message'],
  56749. 'file' => isset($cause['context']['file']) ?
  56750. $cause['context']['file'] :
  56751. 'unknown',
  56752. 'line' => isset($cause['context']['line']) ?
  56753. $cause['context']['line'] :
  56754. 'unknown',
  56755. );
  56756. }
  56757. }
  56758. }
  56759. }
  56760. public function getTraceSafe()
  56761. {
  56762. if (!isset($this->_trace)) {
  56763. $this->_trace = $this->getTrace();
  56764. if (empty($this->_trace)) {
  56765. $backtrace = debug_backtrace();
  56766. $this->_trace = array($backtrace[count($backtrace)-1]);
  56767. }
  56768. }
  56769. return $this->_trace;
  56770. }
  56771. public function getErrorClass()
  56772. {
  56773. $trace = $this->getTraceSafe();
  56774. return $trace[0]['class'];
  56775. }
  56776. public function getErrorMethod()
  56777. {
  56778. $trace = $this->getTraceSafe();
  56779. return $trace[0]['function'];
  56780. }
  56781. public function __toString()
  56782. {
  56783. if (isset($_SERVER['REQUEST_URI'])) {
  56784. return $this->toHtml();
  56785. }
  56786. return $this->toText();
  56787. }
  56788. public function toHtml()
  56789. {
  56790. $trace = $this->getTraceSafe();
  56791. $causes = array();
  56792. $this->getCauseMessage($causes);
  56793. $html = '<table style="border: 1px" cellspacing="0">' . "\n";
  56794. foreach ($causes as $i => $cause) {
  56795. $html .= '<tr><td colspan="3" style="background: #ff9999">'
  56796. . str_repeat('-', $i) . ' <b>' . $cause['class'] . '</b>: '
  56797. . htmlspecialchars($cause['message']) . ' in <b>' . $cause['file'] . '</b> '
  56798. . 'on line <b>' . $cause['line'] . '</b>'
  56799. . "</td></tr>\n";
  56800. }
  56801. $html .= '<tr><td colspan="3" style="background-color: #aaaaaa; text-align: center; font-weight: bold;">Exception trace</td></tr>' . "\n"
  56802. . '<tr><td style="text-align: center; background: #cccccc; width:20px; font-weight: bold;">#</td>'
  56803. . '<td style="text-align: center; background: #cccccc; font-weight: bold;">Function</td>'
  56804. . '<td style="text-align: center; background: #cccccc; font-weight: bold;">Location</td></tr>' . "\n";
  56805. foreach ($trace as $k => $v) {
  56806. $html .= '<tr><td style="text-align: center;">' . $k . '</td>'
  56807. . '<td>';
  56808. if (!empty($v['class'])) {
  56809. $html .= $v['class'] . $v['type'];
  56810. }
  56811. $html .= $v['function'];
  56812. $args = array();
  56813. if (!empty($v['args'])) {
  56814. foreach ($v['args'] as $arg) {
  56815. if (is_null($arg)) $args[] = 'null';
  56816. elseif (is_array($arg)) $args[] = 'Array';
  56817. elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')';
  56818. elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false';
  56819. elseif (is_int($arg) || is_double($arg)) $args[] = $arg;
  56820. else {
  56821. $arg = (string)$arg;
  56822. $str = htmlspecialchars(substr($arg, 0, 16));
  56823. if (strlen($arg) > 16) $str .= '&hellip;';
  56824. $args[] = "'" . $str . "'";
  56825. }
  56826. }
  56827. }
  56828. $html .= '(' . implode(', ',$args) . ')'
  56829. . '</td>'
  56830. . '<td>' . (isset($v['file']) ? $v['file'] : 'unknown')
  56831. . ':' . (isset($v['line']) ? $v['line'] : 'unknown')
  56832. . '</td></tr>' . "\n";
  56833. }
  56834. $html .= '<tr><td style="text-align: center;">' . ($k+1) . '</td>'
  56835. . '<td>{main}</td>'
  56836. . '<td>&nbsp;</td></tr>' . "\n"
  56837. . '</table>';
  56838. return $html;
  56839. }
  56840. public function toText()
  56841. {
  56842. $causes = array();
  56843. $this->getCauseMessage($causes);
  56844. $causeMsg = '';
  56845. foreach ($causes as $i => $cause) {
  56846. $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': '
  56847. . $cause['message'] . ' in ' . $cause['file']
  56848. . ' on line ' . $cause['line'] . "\n";
  56849. }
  56850. return $causeMsg . $this->getTraceAsString();
  56851. }
  56852. }�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Frontend.php����������������������������������������������������������������������0000664�0001750�0001750�00000014772�14720722517�015322� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  56853. /**
  56854. * PEAR_Frontend, the singleton-based frontend for user input/output
  56855. *
  56856. * PHP versions 4 and 5
  56857. *
  56858. * @category pear
  56859. * @package PEAR
  56860. * @author Greg Beaver <cellog@php.net>
  56861. * @copyright 1997-2009 The Authors
  56862. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  56863. * @link http://pear.php.net/package/PEAR
  56864. * @since File available since Release 1.4.0a1
  56865. */
  56866. /**
  56867. * Include error handling
  56868. */
  56869. //require_once 'PEAR.php';
  56870. /**
  56871. * Which user interface class is being used.
  56872. * @var string class name
  56873. */
  56874. $GLOBALS['_PEAR_FRONTEND_CLASS'] = 'PEAR_Frontend_CLI';
  56875. /**
  56876. * Instance of $_PEAR_Command_uiclass.
  56877. * @var object
  56878. */
  56879. $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = null;
  56880. /**
  56881. * Singleton-based frontend for PEAR user input/output
  56882. *
  56883. * @category pear
  56884. * @package PEAR
  56885. * @author Greg Beaver <cellog@php.net>
  56886. * @copyright 1997-2009 The Authors
  56887. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  56888. * @version Release: 1.10.16
  56889. * @link http://pear.php.net/package/PEAR
  56890. * @since Class available since Release 1.4.0a1
  56891. */
  56892. class PEAR_Frontend extends PEAR
  56893. {
  56894. /**
  56895. * Retrieve the frontend object
  56896. * @return PEAR_Frontend_CLI|PEAR_Frontend_Web|PEAR_Frontend_Gtk
  56897. */
  56898. public static function &singleton($type = null)
  56899. {
  56900. if ($type === null) {
  56901. if (!isset($GLOBALS['_PEAR_FRONTEND_SINGLETON'])) {
  56902. $a = false;
  56903. return $a;
  56904. }
  56905. return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
  56906. }
  56907. $a = PEAR_Frontend::setFrontendClass($type);
  56908. return $a;
  56909. }
  56910. /**
  56911. * Set the frontend class that will be used by calls to {@link singleton()}
  56912. *
  56913. * Frontends are expected to conform to the PEAR naming standard of
  56914. * _ => DIRECTORY_SEPARATOR (PEAR_Frontend_CLI is in PEAR/Frontend/CLI.php)
  56915. * @param string $uiclass full class name
  56916. * @return PEAR_Frontend
  56917. */
  56918. public static function &setFrontendClass($uiclass)
  56919. {
  56920. if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) &&
  56921. is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], $uiclass)) {
  56922. return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
  56923. }
  56924. if (!class_exists($uiclass)) {
  56925. $file = str_replace('_', '/', $uiclass) . '.php';
  56926. if (PEAR_Frontend::isIncludeable($file)) {
  56927. include_once $file;
  56928. }
  56929. }
  56930. if (class_exists($uiclass)) {
  56931. $obj = new $uiclass;
  56932. // quick test to see if this class implements a few of the most
  56933. // important frontend methods
  56934. if (is_a($obj, 'PEAR_Frontend')) {
  56935. $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$obj;
  56936. $GLOBALS['_PEAR_FRONTEND_CLASS'] = $uiclass;
  56937. return $obj;
  56938. }
  56939. $err = PEAR::raiseError("not a frontend class: $uiclass");
  56940. return $err;
  56941. }
  56942. $err = PEAR::raiseError("no such class: $uiclass");
  56943. return $err;
  56944. }
  56945. /**
  56946. * Set the frontend class that will be used by calls to {@link singleton()}
  56947. *
  56948. * Frontends are expected to be a descendant of PEAR_Frontend
  56949. * @param PEAR_Frontend
  56950. * @return PEAR_Frontend
  56951. */
  56952. public static function &setFrontendObject($uiobject)
  56953. {
  56954. if (is_object($GLOBALS['_PEAR_FRONTEND_SINGLETON']) &&
  56955. is_a($GLOBALS['_PEAR_FRONTEND_SINGLETON'], get_class($uiobject))) {
  56956. return $GLOBALS['_PEAR_FRONTEND_SINGLETON'];
  56957. }
  56958. if (!is_a($uiobject, 'PEAR_Frontend')) {
  56959. $err = PEAR::raiseError('not a valid frontend class: (' .
  56960. get_class($uiobject) . ')');
  56961. return $err;
  56962. }
  56963. $GLOBALS['_PEAR_FRONTEND_SINGLETON'] = &$uiobject;
  56964. $GLOBALS['_PEAR_FRONTEND_CLASS'] = get_class($uiobject);
  56965. return $uiobject;
  56966. }
  56967. /**
  56968. * @param string $path relative or absolute include path
  56969. * @return boolean
  56970. */
  56971. public static function isIncludeable($path)
  56972. {
  56973. if (file_exists($path) && is_readable($path)) {
  56974. return true;
  56975. }
  56976. $fp = @fopen($path, 'r', true);
  56977. if ($fp) {
  56978. fclose($fp);
  56979. return true;
  56980. }
  56981. return false;
  56982. }
  56983. /**
  56984. * @param PEAR_Config
  56985. */
  56986. function setConfig(&$config)
  56987. {
  56988. }
  56989. /**
  56990. * This can be overridden to allow session-based temporary file management
  56991. *
  56992. * By default, all files are deleted at the end of a session. The web installer
  56993. * needs to be able to sustain a list over many sessions in order to support
  56994. * user interaction with install scripts
  56995. */
  56996. static function addTempFile($file)
  56997. {
  56998. $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
  56999. }
  57000. /**
  57001. * Log an action
  57002. *
  57003. * @param string $msg the message to log
  57004. * @param boolean $append_crlf
  57005. * @return boolean true
  57006. * @abstract
  57007. */
  57008. function log($msg, $append_crlf = true)
  57009. {
  57010. }
  57011. /**
  57012. * Run a post-installation script
  57013. *
  57014. * @param array $scripts array of post-install scripts
  57015. * @abstract
  57016. */
  57017. function runPostinstallScripts(&$scripts)
  57018. {
  57019. }
  57020. /**
  57021. * Display human-friendly output formatted depending on the
  57022. * $command parameter.
  57023. *
  57024. * This should be able to handle basic output data with no command
  57025. * @param mixed $data data structure containing the information to display
  57026. * @param string $command command from which this method was called
  57027. * @abstract
  57028. */
  57029. function outputData($data, $command = '_default')
  57030. {
  57031. }
  57032. /**
  57033. * Display a modal form dialog and return the given input
  57034. *
  57035. * A frontend that requires multiple requests to retrieve and process
  57036. * data must take these needs into account, and implement the request
  57037. * handling code.
  57038. * @param string $command command from which this method was called
  57039. * @param array $prompts associative array. keys are the input field names
  57040. * and values are the description
  57041. * @param array $types array of input field types (text, password,
  57042. * etc.) keys have to be the same like in $prompts
  57043. * @param array $defaults array of default values. again keys have
  57044. * to be the same like in $prompts. Do not depend
  57045. * on a default value being set.
  57046. * @return array input sent by the user
  57047. * @abstract
  57048. */
  57049. function userDialog($command, $prompts, $types = array(), $defaults = array())
  57050. {
  57051. }
  57052. }
  57053. ������PEAR-1.10.16/PEAR/Installer.php���������������������������������������������������������������������0000664�0001750�0001750�00000210711�14720722517�015467� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  57054. /**
  57055. * PEAR_Installer
  57056. *
  57057. * PHP versions 4 and 5
  57058. *
  57059. * @category pear
  57060. * @package PEAR
  57061. * @author Stig Bakken <ssb@php.net>
  57062. * @author Tomas V.V. Cox <cox@idecnet.com>
  57063. * @author Martin Jansen <mj@php.net>
  57064. * @author Greg Beaver <cellog@php.net>
  57065. * @copyright 1997-2009 The Authors
  57066. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  57067. * @link http://pear.php.net/package/PEAR
  57068. * @since File available since Release 0.1
  57069. */
  57070. /**
  57071. * Used for installation groups in package.xml 2.0 and platform exceptions
  57072. */
  57073. require_once 'OS/Guess.php';
  57074. require_once 'PEAR/Downloader.php';
  57075. define('PEAR_INSTALLER_NOBINARY', -240);
  57076. /**
  57077. * Administration class used to install PEAR packages and maintain the
  57078. * installed package database.
  57079. *
  57080. * @category pear
  57081. * @package PEAR
  57082. * @author Stig Bakken <ssb@php.net>
  57083. * @author Tomas V.V. Cox <cox@idecnet.com>
  57084. * @author Martin Jansen <mj@php.net>
  57085. * @author Greg Beaver <cellog@php.net>
  57086. * @copyright 1997-2009 The Authors
  57087. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  57088. * @version Release: 1.10.16
  57089. * @link http://pear.php.net/package/PEAR
  57090. * @since Class available since Release 0.1
  57091. */
  57092. class PEAR_Installer extends PEAR_Downloader
  57093. {
  57094. // {{{ properties
  57095. /** name of the package directory, for example Foo-1.0
  57096. * @var string
  57097. */
  57098. var $pkgdir;
  57099. /** directory where PHP code files go
  57100. * @var string
  57101. */
  57102. var $phpdir;
  57103. /** directory where PHP extension files go
  57104. * @var string
  57105. */
  57106. var $extdir;
  57107. /** directory where documentation goes
  57108. * @var string
  57109. */
  57110. var $docdir;
  57111. /** installation root directory (ala PHP's INSTALL_ROOT or
  57112. * automake's DESTDIR
  57113. * @var string
  57114. */
  57115. var $installroot = '';
  57116. /** debug level
  57117. * @var int
  57118. */
  57119. var $debug = 1;
  57120. /** temporary directory
  57121. * @var string
  57122. */
  57123. var $tmpdir;
  57124. /**
  57125. * PEAR_Registry object used by the installer
  57126. * @var PEAR_Registry
  57127. */
  57128. var $registry;
  57129. /**
  57130. * array of PEAR_Downloader_Packages
  57131. * @var array
  57132. */
  57133. var $_downloadedPackages;
  57134. /** List of file transactions queued for an install/upgrade/uninstall.
  57135. *
  57136. * Format:
  57137. * array(
  57138. * 0 => array("rename => array("from-file", "to-file")),
  57139. * 1 => array("delete" => array("file-to-delete")),
  57140. * ...
  57141. * )
  57142. *
  57143. * @var array
  57144. */
  57145. var $file_operations = array();
  57146. // }}}
  57147. // {{{ constructor
  57148. /**
  57149. * PEAR_Installer constructor.
  57150. *
  57151. * @param object $ui user interface object (instance of PEAR_Frontend_*)
  57152. *
  57153. * @access public
  57154. */
  57155. function __construct(&$ui)
  57156. {
  57157. parent::__construct($ui, array(), null);
  57158. $this->setFrontendObject($ui);
  57159. $this->debug = $this->config->get('verbose');
  57160. }
  57161. function setOptions($options)
  57162. {
  57163. $this->_options = $options;
  57164. }
  57165. function setConfig(&$config)
  57166. {
  57167. $this->config = &$config;
  57168. $this->_registry = &$config->getRegistry();
  57169. }
  57170. // }}}
  57171. function _removeBackups($files)
  57172. {
  57173. foreach ($files as $path) {
  57174. $this->addFileOperation('removebackup', array($path));
  57175. }
  57176. }
  57177. // {{{ _deletePackageFiles()
  57178. /**
  57179. * Delete a package's installed files, does not remove empty directories.
  57180. *
  57181. * @param string package name
  57182. * @param string channel name
  57183. * @param bool if true, then files are backed up first
  57184. * @return bool TRUE on success, or a PEAR error on failure
  57185. * @access protected
  57186. */
  57187. function _deletePackageFiles($package, $channel = false, $backup = false)
  57188. {
  57189. if (!$channel) {
  57190. $channel = 'pear.php.net';
  57191. }
  57192. if (!strlen($package)) {
  57193. return $this->raiseError("No package to uninstall given");
  57194. }
  57195. if (strtolower($package) == 'pear' && $channel == 'pear.php.net') {
  57196. // to avoid race conditions, include all possible needed files
  57197. require_once 'PEAR/Task/Common.php';
  57198. require_once 'PEAR/Task/Replace.php';
  57199. require_once 'PEAR/Task/Unixeol.php';
  57200. require_once 'PEAR/Task/Windowseol.php';
  57201. require_once 'PEAR/PackageFile/v1.php';
  57202. require_once 'PEAR/PackageFile/v2.php';
  57203. require_once 'PEAR/PackageFile/Generator/v1.php';
  57204. require_once 'PEAR/PackageFile/Generator/v2.php';
  57205. }
  57206. $filelist = $this->_registry->packageInfo($package, 'filelist', $channel);
  57207. if ($filelist == null) {
  57208. return $this->raiseError("$channel/$package not installed");
  57209. }
  57210. $ret = array();
  57211. foreach ($filelist as $file => $props) {
  57212. if (empty($props['installed_as'])) {
  57213. continue;
  57214. }
  57215. $path = $props['installed_as'];
  57216. if ($backup) {
  57217. $this->addFileOperation('backup', array($path));
  57218. $ret[] = $path;
  57219. }
  57220. $this->addFileOperation('delete', array($path));
  57221. }
  57222. if ($backup) {
  57223. return $ret;
  57224. }
  57225. return true;
  57226. }
  57227. // }}}
  57228. // {{{ _installFile()
  57229. /**
  57230. * @param string filename
  57231. * @param array attributes from <file> tag in package.xml
  57232. * @param string path to install the file in
  57233. * @param array options from command-line
  57234. * @access private
  57235. */
  57236. function _installFile($file, $atts, $tmp_path, $options)
  57237. {
  57238. // {{{ return if this file is meant for another platform
  57239. static $os;
  57240. if (!isset($this->_registry)) {
  57241. $this->_registry = &$this->config->getRegistry();
  57242. }
  57243. if (isset($atts['platform'])) {
  57244. if (empty($os)) {
  57245. $os = new OS_Guess();
  57246. }
  57247. if (strlen($atts['platform']) && $atts['platform'][0] == '!') {
  57248. $negate = true;
  57249. $platform = substr($atts['platform'], 1);
  57250. } else {
  57251. $negate = false;
  57252. $platform = $atts['platform'];
  57253. }
  57254. if ((bool) $os->matchSignature($platform) === $negate) {
  57255. $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")");
  57256. return PEAR_INSTALLER_SKIPPED;
  57257. }
  57258. }
  57259. // }}}
  57260. $channel = $this->pkginfo->getChannel();
  57261. // {{{ assemble the destination paths
  57262. switch ($atts['role']) {
  57263. case 'src':
  57264. case 'extsrc':
  57265. $this->source_files++;
  57266. return;
  57267. case 'doc':
  57268. case 'data':
  57269. case 'test':
  57270. $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel) .
  57271. DIRECTORY_SEPARATOR . $this->pkginfo->getPackage();
  57272. unset($atts['baseinstalldir']);
  57273. break;
  57274. case 'ext':
  57275. case 'php':
  57276. $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel);
  57277. break;
  57278. case 'script':
  57279. $dest_dir = $this->config->get('bin_dir', null, $channel);
  57280. break;
  57281. default:
  57282. return $this->raiseError("Invalid role `$atts[role]' for file $file");
  57283. }
  57284. $save_destdir = $dest_dir;
  57285. if (!empty($atts['baseinstalldir'])) {
  57286. $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
  57287. }
  57288. if (dirname($file) != '.' && empty($atts['install-as'])) {
  57289. $dest_dir .= DIRECTORY_SEPARATOR . dirname($file);
  57290. }
  57291. if (empty($atts['install-as'])) {
  57292. $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file);
  57293. } else {
  57294. $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as'];
  57295. }
  57296. $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file;
  57297. // Clean up the DIRECTORY_SEPARATOR mess
  57298. $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
  57299. list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
  57300. array(DIRECTORY_SEPARATOR,
  57301. DIRECTORY_SEPARATOR,
  57302. DIRECTORY_SEPARATOR),
  57303. array($dest_file, $orig_file));
  57304. $final_dest_file = $installed_as = $dest_file;
  57305. if (isset($this->_options['packagingroot'])) {
  57306. $installedas_dest_dir = dirname($final_dest_file);
  57307. $installedas_dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
  57308. $final_dest_file = $this->_prependPath($final_dest_file, $this->_options['packagingroot']);
  57309. } else {
  57310. $installedas_dest_dir = dirname($final_dest_file);
  57311. $installedas_dest_file = $installedas_dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
  57312. }
  57313. $dest_dir = dirname($final_dest_file);
  57314. $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
  57315. if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
  57316. return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
  57317. }
  57318. // }}}
  57319. if (empty($this->_options['register-only']) &&
  57320. (!file_exists($dest_dir) || !is_dir($dest_dir))) {
  57321. if (!$this->mkDirHier($dest_dir)) {
  57322. return $this->raiseError("failed to mkdir $dest_dir",
  57323. PEAR_INSTALLER_FAILED);
  57324. }
  57325. $this->log(3, "+ mkdir $dest_dir");
  57326. }
  57327. // pretty much nothing happens if we are only registering the install
  57328. if (empty($this->_options['register-only'])) {
  57329. if (empty($atts['replacements'])) {
  57330. if (!file_exists($orig_file)) {
  57331. return $this->raiseError("file $orig_file does not exist",
  57332. PEAR_INSTALLER_FAILED);
  57333. }
  57334. if (!@copy($orig_file, $dest_file)) {
  57335. return $this->raiseError(
  57336. "failed to write $dest_file: " . error_get_last()["message"],
  57337. PEAR_INSTALLER_FAILED);
  57338. }
  57339. $this->log(3, "+ cp $orig_file $dest_file");
  57340. if (isset($atts['md5sum'])) {
  57341. $md5sum = md5_file($dest_file);
  57342. }
  57343. } else {
  57344. // {{{ file with replacements
  57345. if (!file_exists($orig_file)) {
  57346. return $this->raiseError("file does not exist",
  57347. PEAR_INSTALLER_FAILED);
  57348. }
  57349. $contents = file_get_contents($orig_file);
  57350. if ($contents === false) {
  57351. $contents = '';
  57352. }
  57353. if (isset($atts['md5sum'])) {
  57354. $md5sum = md5($contents);
  57355. }
  57356. $subst_from = $subst_to = array();
  57357. foreach ($atts['replacements'] as $a) {
  57358. $to = '';
  57359. if ($a['type'] == 'php-const') {
  57360. if (preg_match('/^[a-z0-9_]+\\z/i', $a['to'])) {
  57361. eval("\$to = $a[to];");
  57362. } else {
  57363. if (!isset($options['soft'])) {
  57364. $this->log(0, "invalid php-const replacement: $a[to]");
  57365. }
  57366. continue;
  57367. }
  57368. } elseif ($a['type'] == 'pear-config') {
  57369. if ($a['to'] == 'master_server') {
  57370. $chan = $this->_registry->getChannel($channel);
  57371. if (!PEAR::isError($chan)) {
  57372. $to = $chan->getServer();
  57373. } else {
  57374. $to = $this->config->get($a['to'], null, $channel);
  57375. }
  57376. } else {
  57377. $to = $this->config->get($a['to'], null, $channel);
  57378. }
  57379. if (is_null($to)) {
  57380. if (!isset($options['soft'])) {
  57381. $this->log(0, "invalid pear-config replacement: $a[to]");
  57382. }
  57383. continue;
  57384. }
  57385. } elseif ($a['type'] == 'package-info') {
  57386. if ($t = $this->pkginfo->packageInfo($a['to'])) {
  57387. $to = $t;
  57388. } else {
  57389. if (!isset($options['soft'])) {
  57390. $this->log(0, "invalid package-info replacement: $a[to]");
  57391. }
  57392. continue;
  57393. }
  57394. }
  57395. if (!is_null($to)) {
  57396. $subst_from[] = $a['from'];
  57397. $subst_to[] = $to;
  57398. }
  57399. }
  57400. $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file");
  57401. if (sizeof($subst_from)) {
  57402. $contents = str_replace($subst_from, $subst_to, $contents);
  57403. }
  57404. $wp = @fopen($dest_file, "wb");
  57405. if (!is_resource($wp)) {
  57406. return $this->raiseError(
  57407. "failed to create $dest_file: " . error_get_last()["message"],
  57408. PEAR_INSTALLER_FAILED);
  57409. }
  57410. if (@fwrite($wp, $contents) === false) {
  57411. return $this->raiseError(
  57412. "failed writing to $dest_file: " . error_get_last()["message"],
  57413. PEAR_INSTALLER_FAILED);
  57414. }
  57415. fclose($wp);
  57416. // }}}
  57417. }
  57418. // {{{ check the md5
  57419. if (isset($md5sum)) {
  57420. if (strtolower($md5sum) === strtolower($atts['md5sum'])) {
  57421. $this->log(2, "md5sum ok: $final_dest_file");
  57422. } else {
  57423. if (empty($options['force'])) {
  57424. // delete the file
  57425. if (file_exists($dest_file)) {
  57426. unlink($dest_file);
  57427. }
  57428. if (!isset($options['ignore-errors'])) {
  57429. return $this->raiseError("bad md5sum for file $final_dest_file",
  57430. PEAR_INSTALLER_FAILED);
  57431. }
  57432. if (!isset($options['soft'])) {
  57433. $this->log(0, "warning : bad md5sum for file $final_dest_file");
  57434. }
  57435. } else {
  57436. if (!isset($options['soft'])) {
  57437. $this->log(0, "warning : bad md5sum for file $final_dest_file");
  57438. }
  57439. }
  57440. }
  57441. }
  57442. // }}}
  57443. // {{{ set file permissions
  57444. if (!OS_WINDOWS) {
  57445. if ($atts['role'] == 'script') {
  57446. $mode = 0777 & ~(int)octdec($this->config->get('umask'));
  57447. $this->log(3, "+ chmod +x $dest_file");
  57448. } else {
  57449. $mode = 0666 & ~(int)octdec($this->config->get('umask'));
  57450. }
  57451. if ($atts['role'] != 'src') {
  57452. $this->addFileOperation("chmod", array($mode, $dest_file));
  57453. if (!@chmod($dest_file, $mode)) {
  57454. if (!isset($options['soft'])) {
  57455. $this->log(0, "failed to change mode of $dest_file: " .
  57456. error_get_last()["message"]);
  57457. }
  57458. }
  57459. }
  57460. }
  57461. // }}}
  57462. if ($atts['role'] == 'src') {
  57463. rename($dest_file, $final_dest_file);
  57464. $this->log(2, "renamed source file $dest_file to $final_dest_file");
  57465. } else {
  57466. $this->addFileOperation("rename", array($dest_file, $final_dest_file,
  57467. $atts['role'] == 'ext'));
  57468. }
  57469. }
  57470. // Store the full path where the file was installed for easy unistall
  57471. if ($atts['role'] != 'script') {
  57472. $loc = $this->config->get($atts['role'] . '_dir');
  57473. } else {
  57474. $loc = $this->config->get('bin_dir');
  57475. }
  57476. if ($atts['role'] != 'src') {
  57477. $this->addFileOperation("installed_as", array($file, $installed_as,
  57478. $loc,
  57479. dirname(substr($installedas_dest_file, strlen($loc)))));
  57480. }
  57481. //$this->log(2, "installed: $dest_file");
  57482. return PEAR_INSTALLER_OK;
  57483. }
  57484. // }}}
  57485. // {{{ _installFile2()
  57486. /**
  57487. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  57488. * @param string filename
  57489. * @param array attributes from <file> tag in package.xml
  57490. * @param string path to install the file in
  57491. * @param array options from command-line
  57492. * @access private
  57493. */
  57494. function _installFile2(&$pkg, $file, &$real_atts, $tmp_path, $options)
  57495. {
  57496. $atts = $real_atts;
  57497. if (!isset($this->_registry)) {
  57498. $this->_registry = &$this->config->getRegistry();
  57499. }
  57500. $channel = $pkg->getChannel();
  57501. // {{{ assemble the destination paths
  57502. if (!in_array($atts['attribs']['role'],
  57503. PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
  57504. return $this->raiseError('Invalid role `' . $atts['attribs']['role'] .
  57505. "' for file $file");
  57506. }
  57507. $role = &PEAR_Installer_Role::factory($pkg, $atts['attribs']['role'], $this->config);
  57508. $err = $role->setup($this, $pkg, $atts['attribs'], $file);
  57509. if (PEAR::isError($err)) {
  57510. return $err;
  57511. }
  57512. if (!$role->isInstallable()) {
  57513. return;
  57514. }
  57515. $info = $role->processInstallation($pkg, $atts['attribs'], $file, $tmp_path);
  57516. if (PEAR::isError($info)) {
  57517. return $info;
  57518. }
  57519. list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info;
  57520. if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
  57521. return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
  57522. }
  57523. $final_dest_file = $installed_as = $dest_file;
  57524. if (isset($this->_options['packagingroot'])) {
  57525. $final_dest_file = $this->_prependPath($final_dest_file,
  57526. $this->_options['packagingroot']);
  57527. }
  57528. $dest_dir = dirname($final_dest_file);
  57529. $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
  57530. // }}}
  57531. if (empty($this->_options['register-only'])) {
  57532. if (!file_exists($dest_dir) || !is_dir($dest_dir)) {
  57533. if (!$this->mkDirHier($dest_dir)) {
  57534. return $this->raiseError("failed to mkdir $dest_dir",
  57535. PEAR_INSTALLER_FAILED);
  57536. }
  57537. $this->log(3, "+ mkdir $dest_dir");
  57538. }
  57539. }
  57540. $attribs = $atts['attribs'];
  57541. unset($atts['attribs']);
  57542. // pretty much nothing happens if we are only registering the install
  57543. if (empty($this->_options['register-only'])) {
  57544. if (!count($atts)) { // no tasks
  57545. if (!file_exists($orig_file)) {
  57546. return $this->raiseError("file $orig_file does not exist",
  57547. PEAR_INSTALLER_FAILED);
  57548. }
  57549. if (!@copy($orig_file, $dest_file)) {
  57550. return $this->raiseError(
  57551. "failed to write $dest_file: " . error_get_last()["message"],
  57552. PEAR_INSTALLER_FAILED);
  57553. }
  57554. $this->log(3, "+ cp $orig_file $dest_file");
  57555. if (isset($attribs['md5sum'])) {
  57556. $md5sum = md5_file($dest_file);
  57557. }
  57558. } else { // file with tasks
  57559. if (!file_exists($orig_file)) {
  57560. return $this->raiseError("file $orig_file does not exist",
  57561. PEAR_INSTALLER_FAILED);
  57562. }
  57563. $contents = file_get_contents($orig_file);
  57564. if ($contents === false) {
  57565. $contents = '';
  57566. }
  57567. if (isset($attribs['md5sum'])) {
  57568. $md5sum = md5($contents);
  57569. }
  57570. foreach ($atts as $tag => $raw) {
  57571. $tag = str_replace(array($pkg->getTasksNs() . ':', '-'), array('', '_'), $tag);
  57572. $task = "PEAR_Task_$tag";
  57573. $task = new $task($this->config, $this, PEAR_TASK_INSTALL);
  57574. if (!$task->isScript()) { // scripts are only handled after installation
  57575. $task->init($raw, $attribs, $pkg->getLastInstalledVersion());
  57576. $res = $task->startSession($pkg, $contents, $final_dest_file);
  57577. if ($res === false) {
  57578. continue; // skip this file
  57579. }
  57580. if (PEAR::isError($res)) {
  57581. return $res;
  57582. }
  57583. $contents = $res; // save changes
  57584. }
  57585. $wp = @fopen($dest_file, "wb");
  57586. if (!is_resource($wp)) {
  57587. return $this->raiseError(
  57588. "failed to create $dest_file: " . error_get_last()["message"],
  57589. PEAR_INSTALLER_FAILED);
  57590. }
  57591. if (fwrite($wp, $contents) === false) {
  57592. return $this->raiseError(
  57593. "failed writing to $dest_file: " . error_get_last()["message"],
  57594. PEAR_INSTALLER_FAILED);
  57595. }
  57596. fclose($wp);
  57597. }
  57598. }
  57599. // {{{ check the md5
  57600. if (isset($md5sum)) {
  57601. // Make sure the original md5 sum matches with expected
  57602. if (strtolower($md5sum) === strtolower($attribs['md5sum'])) {
  57603. $this->log(2, "md5sum ok: $final_dest_file");
  57604. if (isset($contents)) {
  57605. // set md5 sum based on $content in case any tasks were run.
  57606. $real_atts['attribs']['md5sum'] = md5($contents);
  57607. }
  57608. } else {
  57609. if (empty($options['force'])) {
  57610. // delete the file
  57611. if (file_exists($dest_file)) {
  57612. unlink($dest_file);
  57613. }
  57614. if (!isset($options['ignore-errors'])) {
  57615. return $this->raiseError("bad md5sum for file $final_dest_file",
  57616. PEAR_INSTALLER_FAILED);
  57617. }
  57618. if (!isset($options['soft'])) {
  57619. $this->log(0, "warning : bad md5sum for file $final_dest_file");
  57620. }
  57621. } else {
  57622. if (!isset($options['soft'])) {
  57623. $this->log(0, "warning : bad md5sum for file $final_dest_file");
  57624. }
  57625. }
  57626. }
  57627. } else {
  57628. $real_atts['attribs']['md5sum'] = md5_file($dest_file);
  57629. }
  57630. // }}}
  57631. // {{{ set file permissions
  57632. if (!OS_WINDOWS) {
  57633. if ($role->isExecutable()) {
  57634. $mode = 0777 & ~(int)octdec($this->config->get('umask'));
  57635. $this->log(3, "+ chmod +x $dest_file");
  57636. } else {
  57637. $mode = 0666 & ~(int)octdec($this->config->get('umask'));
  57638. }
  57639. if ($attribs['role'] != 'src') {
  57640. $this->addFileOperation("chmod", array($mode, $dest_file));
  57641. if (!@chmod($dest_file, $mode)) {
  57642. if (!isset($options['soft'])) {
  57643. $this->log(0, "failed to change mode of $dest_file: " .
  57644. error_get_last()["message"]);
  57645. }
  57646. }
  57647. }
  57648. }
  57649. // }}}
  57650. if ($attribs['role'] == 'src') {
  57651. rename($dest_file, $final_dest_file);
  57652. $this->log(2, "renamed source file $dest_file to $final_dest_file");
  57653. } else {
  57654. $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension()));
  57655. }
  57656. }
  57657. // Store the full path where the file was installed for easy uninstall
  57658. if ($attribs['role'] != 'src') {
  57659. $loc = $this->config->get($role->getLocationConfig(), null, $channel);
  57660. $this->addFileOperation('installed_as', array($file, $installed_as,
  57661. $loc,
  57662. dirname(substr($installed_as, strlen($loc)))));
  57663. }
  57664. //$this->log(2, "installed: $dest_file");
  57665. return PEAR_INSTALLER_OK;
  57666. }
  57667. // }}}
  57668. // {{{ addFileOperation()
  57669. /**
  57670. * Add a file operation to the current file transaction.
  57671. *
  57672. * @see startFileTransaction()
  57673. * @param string $type This can be one of:
  57674. * - rename: rename a file ($data has 3 values)
  57675. * - backup: backup an existing file ($data has 1 value)
  57676. * - removebackup: clean up backups created during install ($data has 1 value)
  57677. * - chmod: change permissions on a file ($data has 2 values)
  57678. * - delete: delete a file ($data has 1 value)
  57679. * - rmdir: delete a directory if empty ($data has 1 value)
  57680. * - installed_as: mark a file as installed ($data has 4 values).
  57681. * @param array $data For all file operations, this array must contain the
  57682. * full path to the file or directory that is being operated on. For
  57683. * the rename command, the first parameter must be the file to rename,
  57684. * the second its new name, the third whether this is a PHP extension.
  57685. *
  57686. * The installed_as operation contains 4 elements in this order:
  57687. * 1. Filename as listed in the filelist element from package.xml
  57688. * 2. Full path to the installed file
  57689. * 3. Full path from the php_dir configuration variable used in this
  57690. * installation
  57691. * 4. Relative path from the php_dir that this file is installed in
  57692. */
  57693. function addFileOperation($type, $data)
  57694. {
  57695. if (!is_array($data)) {
  57696. return $this->raiseError('Internal Error: $data in addFileOperation'
  57697. . ' must be an array, was ' . gettype($data));
  57698. }
  57699. if ($type == 'chmod') {
  57700. $octmode = decoct($data[0]);
  57701. $this->log(3, "adding to transaction: $type $octmode $data[1]");
  57702. } else {
  57703. $this->log(3, "adding to transaction: $type " . implode(" ", $data));
  57704. }
  57705. $this->file_operations[] = array($type, $data);
  57706. }
  57707. // }}}
  57708. // {{{ startFileTransaction()
  57709. function startFileTransaction($rollback_in_case = false)
  57710. {
  57711. if (count($this->file_operations) && $rollback_in_case) {
  57712. $this->rollbackFileTransaction();
  57713. }
  57714. $this->file_operations = array();
  57715. }
  57716. // }}}
  57717. // {{{ commitFileTransaction()
  57718. function commitFileTransaction()
  57719. {
  57720. // {{{ first, check permissions and such manually
  57721. $errors = array();
  57722. foreach ($this->file_operations as $key => $tr) {
  57723. list($type, $data) = $tr;
  57724. switch ($type) {
  57725. case 'rename':
  57726. if (!file_exists($data[0])) {
  57727. $errors[] = "cannot rename file $data[0], doesn't exist";
  57728. }
  57729. // check that dest dir. is writable
  57730. if (!is_writable(dirname($data[1]))) {
  57731. $errors[] = "permission denied ($type): $data[1]";
  57732. }
  57733. break;
  57734. case 'chmod':
  57735. // check that file is writable
  57736. if (!is_writable($data[1])) {
  57737. $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]);
  57738. }
  57739. break;
  57740. case 'delete':
  57741. if (!file_exists($data[0])) {
  57742. $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted");
  57743. }
  57744. // check that directory is writable
  57745. if (file_exists($data[0])) {
  57746. if (!is_writable(dirname($data[0]))) {
  57747. $errors[] = "permission denied ($type): $data[0]";
  57748. } else {
  57749. // make sure the file to be deleted can be opened for writing
  57750. $fp = false;
  57751. if (!is_dir($data[0]) &&
  57752. (!is_writable($data[0]) || !($fp = @fopen($data[0], 'a')))) {
  57753. $errors[] = "permission denied ($type): $data[0]";
  57754. } elseif ($fp) {
  57755. fclose($fp);
  57756. }
  57757. }
  57758. /* Verify we are not deleting a file owned by another package
  57759. * This can happen when a file moves from package A to B in
  57760. * an upgrade ala http://pear.php.net/17986
  57761. */
  57762. $info = array(
  57763. 'package' => strtolower($this->pkginfo->getName()),
  57764. 'channel' => strtolower($this->pkginfo->getChannel()),
  57765. );
  57766. $result = $this->_registry->checkFileMap($data[0], $info, '1.1');
  57767. if (is_array($result)) {
  57768. $res = array_diff($result, $info);
  57769. if (!empty($res)) {
  57770. $new = $this->_registry->getPackage($result[1], $result[0]);
  57771. $this->file_operations[$key] = false;
  57772. $pkginfoName = $this->pkginfo->getName();
  57773. $newChannel = $new->getChannel();
  57774. $newPackage = $new->getName();
  57775. $this->log(3, "file $data[0] was scheduled for removal from $pkginfoName but is owned by $newChannel/$newPackage, removal has been cancelled.");
  57776. }
  57777. }
  57778. }
  57779. break;
  57780. }
  57781. }
  57782. // }}}
  57783. $n = count($this->file_operations);
  57784. $this->log(2, "about to commit $n file operations for " . $this->pkginfo->getName());
  57785. $m = count($errors);
  57786. if ($m > 0) {
  57787. foreach ($errors as $error) {
  57788. if (!isset($this->_options['soft'])) {
  57789. $this->log(1, $error);
  57790. }
  57791. }
  57792. if (!isset($this->_options['ignore-errors'])) {
  57793. return false;
  57794. }
  57795. }
  57796. $this->_dirtree = array();
  57797. // {{{ really commit the transaction
  57798. foreach ($this->file_operations as $i => $tr) {
  57799. if (!$tr) {
  57800. // support removal of non-existing backups
  57801. continue;
  57802. }
  57803. list($type, $data) = $tr;
  57804. switch ($type) {
  57805. case 'backup':
  57806. if (!file_exists($data[0])) {
  57807. $this->file_operations[$i] = false;
  57808. break;
  57809. }
  57810. if (!@copy($data[0], $data[0] . '.bak')) {
  57811. $this->log(1, 'Could not copy ' . $data[0] . ' to ' . $data[0] .
  57812. '.bak ' . error_get_last()["message"]);
  57813. return false;
  57814. }
  57815. $this->log(3, "+ backup $data[0] to $data[0].bak");
  57816. break;
  57817. case 'removebackup':
  57818. if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
  57819. unlink($data[0] . '.bak');
  57820. $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
  57821. }
  57822. break;
  57823. case 'rename':
  57824. $test = file_exists($data[1]) ? @unlink($data[1]) : null;
  57825. if (!$test && file_exists($data[1])) {
  57826. if ($data[2]) {
  57827. $extra = ', this extension must be installed manually. Rename to "' .
  57828. basename($data[1]) . '"';
  57829. } else {
  57830. $extra = '';
  57831. }
  57832. if (!isset($this->_options['soft'])) {
  57833. $this->log(1, 'Could not delete ' . $data[1] . ', cannot rename ' .
  57834. $data[0] . $extra);
  57835. }
  57836. if (!isset($this->_options['ignore-errors'])) {
  57837. return false;
  57838. }
  57839. }
  57840. // permissions issues with rename - copy() is far superior
  57841. $perms = @fileperms($data[0]);
  57842. if (!@copy($data[0], $data[1])) {
  57843. $this->log(1, 'Could not rename ' . $data[0] . ' to ' . $data[1] .
  57844. ' ' . error_get_last()["message"]);
  57845. return false;
  57846. }
  57847. // copy over permissions, otherwise they are lost
  57848. @chmod($data[1], $perms);
  57849. @unlink($data[0]);
  57850. $this->log(3, "+ mv $data[0] $data[1]");
  57851. break;
  57852. case 'chmod':
  57853. if (!@chmod($data[1], $data[0])) {
  57854. $this->log(1, 'Could not chmod ' . $data[1] . ' to ' .
  57855. decoct($data[0]) . ' ' . error_get_last()["message"]);
  57856. return false;
  57857. }
  57858. $octmode = decoct($data[0]);
  57859. $this->log(3, "+ chmod $octmode $data[1]");
  57860. break;
  57861. case 'delete':
  57862. if (file_exists($data[0])) {
  57863. if (!@unlink($data[0])) {
  57864. $this->log(1, 'Could not delete ' . $data[0] . ' ' .
  57865. error_get_last()["message"]);
  57866. return false;
  57867. }
  57868. $this->log(3, "+ rm $data[0]");
  57869. }
  57870. break;
  57871. case 'rmdir':
  57872. if (file_exists($data[0])) {
  57873. do {
  57874. $testme = opendir($data[0]);
  57875. while (false !== ($entry = readdir($testme))) {
  57876. if ($entry == '.' || $entry == '..') {
  57877. continue;
  57878. }
  57879. closedir($testme);
  57880. break 2; // this directory is not empty and can't be
  57881. // deleted
  57882. }
  57883. closedir($testme);
  57884. if (!@rmdir($data[0])) {
  57885. $this->log(1, 'Could not rmdir ' . $data[0] . ' ' .
  57886. error_get_last()["message"]);
  57887. return false;
  57888. }
  57889. $this->log(3, "+ rmdir $data[0]");
  57890. } while (false);
  57891. }
  57892. break;
  57893. case 'installed_as':
  57894. $this->pkginfo->setInstalledAs($data[0], $data[1]);
  57895. if (!isset($this->_dirtree[dirname($data[1])])) {
  57896. $this->_dirtree[dirname($data[1])] = true;
  57897. $this->pkginfo->setDirtree(dirname($data[1]));
  57898. while(!empty($data[3]) && dirname($data[3]) != $data[3] &&
  57899. $data[3] != '/' && $data[3] != '\\') {
  57900. $this->pkginfo->setDirtree($pp =
  57901. $this->_prependPath($data[3], $data[2]));
  57902. $this->_dirtree[$pp] = true;
  57903. $data[3] = dirname($data[3]);
  57904. }
  57905. }
  57906. break;
  57907. }
  57908. }
  57909. // }}}
  57910. $this->log(2, "successfully committed $n file operations");
  57911. $this->file_operations = array();
  57912. return true;
  57913. }
  57914. // }}}
  57915. // {{{ rollbackFileTransaction()
  57916. function rollbackFileTransaction()
  57917. {
  57918. $n = count($this->file_operations);
  57919. $this->log(2, "rolling back $n file operations");
  57920. foreach ($this->file_operations as $tr) {
  57921. list($type, $data) = $tr;
  57922. switch ($type) {
  57923. case 'backup':
  57924. if (file_exists($data[0] . '.bak')) {
  57925. if (file_exists($data[0] && is_writable($data[0]))) {
  57926. unlink($data[0]);
  57927. }
  57928. @copy($data[0] . '.bak', $data[0]);
  57929. $this->log(3, "+ restore $data[0] from $data[0].bak");
  57930. }
  57931. break;
  57932. case 'removebackup':
  57933. if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
  57934. unlink($data[0] . '.bak');
  57935. $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
  57936. }
  57937. break;
  57938. case 'rename':
  57939. @unlink($data[0]);
  57940. $this->log(3, "+ rm $data[0]");
  57941. break;
  57942. case 'mkdir':
  57943. @rmdir($data[0]);
  57944. $this->log(3, "+ rmdir $data[0]");
  57945. break;
  57946. case 'chmod':
  57947. break;
  57948. case 'delete':
  57949. break;
  57950. case 'installed_as':
  57951. $this->pkginfo->setInstalledAs($data[0], false);
  57952. break;
  57953. }
  57954. }
  57955. $this->pkginfo->resetDirtree();
  57956. $this->file_operations = array();
  57957. }
  57958. // }}}
  57959. // {{{ mkDirHier($dir)
  57960. function mkDirHier($dir)
  57961. {
  57962. $this->addFileOperation('mkdir', array($dir));
  57963. return parent::mkDirHier($dir);
  57964. }
  57965. // }}}
  57966. // {{{ _parsePackageXml()
  57967. function _parsePackageXml(&$descfile)
  57968. {
  57969. // Parse xml file -----------------------------------------------
  57970. $pkg = new PEAR_PackageFile($this->config, $this->debug);
  57971. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  57972. $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING);
  57973. PEAR::staticPopErrorHandling();
  57974. if (PEAR::isError($p)) {
  57975. if (is_array($p->getUserInfo())) {
  57976. foreach ($p->getUserInfo() as $err) {
  57977. $loglevel = $err['level'] == 'error' ? 0 : 1;
  57978. if (!isset($this->_options['soft'])) {
  57979. $this->log($loglevel, ucfirst($err['level']) . ': ' . $err['message']);
  57980. }
  57981. }
  57982. }
  57983. return $this->raiseError('Installation failed: invalid package file');
  57984. }
  57985. $descfile = $p->getPackageFile();
  57986. return $p;
  57987. }
  57988. // }}}
  57989. /**
  57990. * Set the list of PEAR_Downloader_Package objects to allow more sane
  57991. * dependency validation
  57992. * @param array
  57993. */
  57994. function setDownloadedPackages(&$pkgs)
  57995. {
  57996. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  57997. $err = $this->analyzeDependencies($pkgs);
  57998. PEAR::popErrorHandling();
  57999. if (PEAR::isError($err)) {
  58000. return $err;
  58001. }
  58002. $this->_downloadedPackages = &$pkgs;
  58003. }
  58004. /**
  58005. * Set the list of PEAR_Downloader_Package objects to allow more sane
  58006. * dependency validation
  58007. * @param array
  58008. */
  58009. function setUninstallPackages(&$pkgs)
  58010. {
  58011. $this->_downloadedPackages = &$pkgs;
  58012. }
  58013. function getInstallPackages()
  58014. {
  58015. return $this->_downloadedPackages;
  58016. }
  58017. // {{{ install()
  58018. /**
  58019. * Installs the files within the package file specified.
  58020. *
  58021. * @param string|PEAR_Downloader_Package $pkgfile path to the package file,
  58022. * or a pre-initialized packagefile object
  58023. * @param array $options
  58024. * recognized options:
  58025. * - installroot : optional prefix directory for installation
  58026. * - force : force installation
  58027. * - register-only : update registry but don't install files
  58028. * - upgrade : upgrade existing install
  58029. * - soft : fail silently
  58030. * - nodeps : ignore dependency conflicts/missing dependencies
  58031. * - alldeps : install all dependencies
  58032. * - onlyreqdeps : install only required dependencies
  58033. *
  58034. * @return array|PEAR_Error package info if successful
  58035. */
  58036. function install($pkgfile, $options = array())
  58037. {
  58038. $this->_options = $options;
  58039. $this->_registry = &$this->config->getRegistry();
  58040. if (is_object($pkgfile)) {
  58041. $dlpkg = &$pkgfile;
  58042. $pkg = $pkgfile->getPackageFile();
  58043. $pkgfile = $pkg->getArchiveFile();
  58044. $descfile = $pkg->getPackageFile();
  58045. } else {
  58046. $descfile = $pkgfile;
  58047. $pkg = $this->_parsePackageXml($descfile);
  58048. if (PEAR::isError($pkg)) {
  58049. return $pkg;
  58050. }
  58051. }
  58052. $tmpdir = dirname($descfile);
  58053. if (realpath($descfile) != realpath($pkgfile)) {
  58054. // Use the temp_dir since $descfile can contain the download dir path
  58055. $tmpdir = $this->config->get('temp_dir', null, 'pear.php.net');
  58056. $tmpdir = System::mktemp('-d -t "' . $tmpdir . '"');
  58057. $tar = new Archive_Tar($pkgfile);
  58058. if (!$tar->extract($tmpdir)) {
  58059. return $this->raiseError("unable to unpack $pkgfile");
  58060. }
  58061. }
  58062. $pkgname = $pkg->getName();
  58063. $channel = $pkg->getChannel();
  58064. if (isset($options['installroot'])) {
  58065. $this->config->setInstallRoot($options['installroot']);
  58066. $this->_registry = &$this->config->getRegistry();
  58067. $installregistry = &$this->_registry;
  58068. $this->installroot = ''; // all done automagically now
  58069. $php_dir = $this->config->get('php_dir', null, $channel);
  58070. } else {
  58071. $this->config->setInstallRoot(false);
  58072. $this->_registry = &$this->config->getRegistry();
  58073. if (isset($this->_options['packagingroot'])) {
  58074. $regdir = $this->_prependPath(
  58075. $this->config->get('php_dir', null, 'pear.php.net'),
  58076. $this->_options['packagingroot']);
  58077. $metadata_dir = $this->config->get('metadata_dir', null, 'pear.php.net');
  58078. if ($metadata_dir) {
  58079. $metadata_dir = $this->_prependPath(
  58080. $metadata_dir,
  58081. $this->_options['packagingroot']);
  58082. }
  58083. $packrootphp_dir = $this->_prependPath(
  58084. $this->config->get('php_dir', null, $channel),
  58085. $this->_options['packagingroot']);
  58086. $installregistry = new PEAR_Registry($regdir, false, false, $metadata_dir);
  58087. if (!$installregistry->channelExists($channel, true)) {
  58088. // we need to fake a channel-discover of this channel
  58089. $chanobj = $this->_registry->getChannel($channel, true);
  58090. $installregistry->addChannel($chanobj);
  58091. }
  58092. $php_dir = $packrootphp_dir;
  58093. } else {
  58094. $installregistry = &$this->_registry;
  58095. $php_dir = $this->config->get('php_dir', null, $channel);
  58096. }
  58097. $this->installroot = '';
  58098. }
  58099. // {{{ checks to do when not in "force" mode
  58100. if (empty($options['force']) &&
  58101. (file_exists($this->config->get('php_dir')) &&
  58102. is_dir($this->config->get('php_dir')))) {
  58103. $testp = $channel == 'pear.php.net' ? $pkgname : array($channel, $pkgname);
  58104. $instfilelist = $pkg->getInstallationFileList(true);
  58105. if (PEAR::isError($instfilelist)) {
  58106. return $instfilelist;
  58107. }
  58108. // ensure we have the most accurate registry
  58109. $installregistry->flushFileMap();
  58110. $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1');
  58111. if (PEAR::isError($test)) {
  58112. return $test;
  58113. }
  58114. if (sizeof($test)) {
  58115. $pkgs = $this->getInstallPackages();
  58116. $found = false;
  58117. foreach ($pkgs as $param) {
  58118. if ($pkg->isSubpackageOf($param)) {
  58119. $found = true;
  58120. break;
  58121. }
  58122. }
  58123. if ($found) {
  58124. // subpackages can conflict with earlier versions of parent packages
  58125. $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel());
  58126. $tmp = $test;
  58127. foreach ($tmp as $file => $info) {
  58128. if (is_array($info)) {
  58129. if (strtolower($info[1]) == strtolower($param->getPackage()) &&
  58130. strtolower($info[0]) == strtolower($param->getChannel())
  58131. ) {
  58132. if (isset($parentreg['filelist'][$file])) {
  58133. unset($parentreg['filelist'][$file]);
  58134. } else{
  58135. $pos = strpos($file, '/');
  58136. $basedir = substr($file, 0, $pos);
  58137. $file2 = substr($file, $pos + 1);
  58138. if (isset($parentreg['filelist'][$file2]['baseinstalldir'])
  58139. && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir
  58140. ) {
  58141. unset($parentreg['filelist'][$file2]);
  58142. }
  58143. }
  58144. unset($test[$file]);
  58145. }
  58146. } else {
  58147. if (strtolower($param->getChannel()) != 'pear.php.net') {
  58148. continue;
  58149. }
  58150. if (strtolower($info) == strtolower($param->getPackage())) {
  58151. if (isset($parentreg['filelist'][$file])) {
  58152. unset($parentreg['filelist'][$file]);
  58153. } else{
  58154. $pos = strpos($file, '/');
  58155. $basedir = substr($file, 0, $pos);
  58156. $file2 = substr($file, $pos + 1);
  58157. if (isset($parentreg['filelist'][$file2]['baseinstalldir'])
  58158. && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir
  58159. ) {
  58160. unset($parentreg['filelist'][$file2]);
  58161. }
  58162. }
  58163. unset($test[$file]);
  58164. }
  58165. }
  58166. }
  58167. $pfk = new PEAR_PackageFile($this->config);
  58168. $parentpkg = &$pfk->fromArray($parentreg);
  58169. $installregistry->updatePackage2($parentpkg);
  58170. }
  58171. if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) {
  58172. $tmp = $test;
  58173. foreach ($tmp as $file => $info) {
  58174. if (is_string($info)) {
  58175. // pear.php.net packages are always stored as strings
  58176. if (strtolower($info) == strtolower($param->getPackage())) {
  58177. // upgrading existing package
  58178. unset($test[$file]);
  58179. }
  58180. }
  58181. }
  58182. }
  58183. if (count($test)) {
  58184. $msg = "$channel/$pkgname: conflicting files found:\n";
  58185. $longest = max(array_map("strlen", array_keys($test)));
  58186. $fmt = "%{$longest}s (%s)\n";
  58187. foreach ($test as $file => $info) {
  58188. if (!is_array($info)) {
  58189. $info = array('pear.php.net', $info);
  58190. }
  58191. $info = $info[0] . '/' . $info[1];
  58192. $msg .= sprintf($fmt, $file, $info);
  58193. }
  58194. if (!isset($options['ignore-errors'])) {
  58195. return $this->raiseError($msg);
  58196. }
  58197. if (!isset($options['soft'])) {
  58198. $this->log(0, "WARNING: $msg");
  58199. }
  58200. }
  58201. }
  58202. }
  58203. // }}}
  58204. $this->startFileTransaction();
  58205. $usechannel = $channel;
  58206. if ($channel == 'pecl.php.net') {
  58207. $test = $installregistry->packageExists($pkgname, $channel);
  58208. if (!$test) {
  58209. $test = $installregistry->packageExists($pkgname, 'pear.php.net');
  58210. $usechannel = 'pear.php.net';
  58211. }
  58212. } else {
  58213. $test = $installregistry->packageExists($pkgname, $channel);
  58214. }
  58215. if (empty($options['upgrade']) && empty($options['soft'])) {
  58216. // checks to do only when installing new packages
  58217. if (empty($options['force']) && $test) {
  58218. return $this->raiseError("$channel/$pkgname is already installed");
  58219. }
  58220. } else {
  58221. // Upgrade
  58222. if ($test) {
  58223. $v1 = $installregistry->packageInfo($pkgname, 'version', $usechannel);
  58224. $v2 = $pkg->getVersion();
  58225. $cmp = version_compare("$v1", "$v2", 'gt');
  58226. if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) {
  58227. return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)");
  58228. }
  58229. }
  58230. }
  58231. // Do cleanups for upgrade and install, remove old release's files first
  58232. if ($test && empty($options['register-only'])) {
  58233. // when upgrading, remove old release's files first:
  58234. if (PEAR::isError($err = $this->_deletePackageFiles($pkgname, $usechannel,
  58235. true))) {
  58236. if (!isset($options['ignore-errors'])) {
  58237. return $this->raiseError($err);
  58238. }
  58239. if (!isset($options['soft'])) {
  58240. $this->log(0, 'WARNING: ' . $err->getMessage());
  58241. }
  58242. } else {
  58243. $backedup = $err;
  58244. }
  58245. }
  58246. // {{{ Copy files to dest dir ---------------------------------------
  58247. // info from the package it self we want to access from _installFile
  58248. $this->pkginfo = &$pkg;
  58249. // used to determine whether we should build any C code
  58250. $this->source_files = 0;
  58251. $savechannel = $this->config->get('default_channel');
  58252. if (empty($options['register-only']) && !is_dir($php_dir)) {
  58253. if (PEAR::isError(System::mkdir(array('-p'), $php_dir))) {
  58254. return $this->raiseError("no installation destination directory '$php_dir'\n");
  58255. }
  58256. }
  58257. if (substr($pkgfile, -4) != '.xml') {
  58258. $tmpdir .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkg->getVersion();
  58259. }
  58260. $this->configSet('default_channel', $channel);
  58261. // {{{ install files
  58262. $ver = $pkg->getPackagexmlVersion();
  58263. if (version_compare($ver, '2.0', '>=')) {
  58264. $filelist = $pkg->getInstallationFilelist();
  58265. } else {
  58266. $filelist = $pkg->getFileList();
  58267. }
  58268. if (PEAR::isError($filelist)) {
  58269. return $filelist;
  58270. }
  58271. $p = &$installregistry->getPackage($pkgname, $channel);
  58272. $dirtree = (empty($options['register-only']) && $p) ? $p->getDirTree() : false;
  58273. $pkg->resetFilelist();
  58274. $pkg->setLastInstalledVersion($installregistry->packageInfo($pkg->getPackage(),
  58275. 'version', $pkg->getChannel()));
  58276. foreach ($filelist as $file => $atts) {
  58277. $this->expectError(PEAR_INSTALLER_FAILED);
  58278. if ($pkg->getPackagexmlVersion() == '1.0') {
  58279. $res = $this->_installFile($file, $atts, $tmpdir, $options);
  58280. } else {
  58281. $res = $this->_installFile2($pkg, $file, $atts, $tmpdir, $options);
  58282. }
  58283. $this->popExpect();
  58284. if (PEAR::isError($res)) {
  58285. if (empty($options['ignore-errors'])) {
  58286. $this->rollbackFileTransaction();
  58287. if ($res->getMessage() == "file does not exist") {
  58288. $this->raiseError("file $file in package.xml does not exist");
  58289. }
  58290. return $this->raiseError($res);
  58291. }
  58292. if (!isset($options['soft'])) {
  58293. $this->log(0, "Warning: " . $res->getMessage());
  58294. }
  58295. }
  58296. $real = isset($atts['attribs']) ? $atts['attribs'] : $atts;
  58297. if ($res == PEAR_INSTALLER_OK && $real['role'] != 'src') {
  58298. // Register files that were installed
  58299. $pkg->installedFile($file, $atts);
  58300. }
  58301. }
  58302. // }}}
  58303. // {{{ compile and install source files
  58304. if ($this->source_files > 0 && empty($options['nobuild'])) {
  58305. $configureoptions = empty($options['configureoptions']) ? '' : $options['configureoptions'];
  58306. if (PEAR::isError($err =
  58307. $this->_compileSourceFiles($savechannel, $pkg, $configureoptions))) {
  58308. return $err;
  58309. }
  58310. }
  58311. // }}}
  58312. if (isset($backedup)) {
  58313. $this->_removeBackups($backedup);
  58314. }
  58315. if (!$this->commitFileTransaction()) {
  58316. $this->rollbackFileTransaction();
  58317. $this->configSet('default_channel', $savechannel);
  58318. return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED);
  58319. }
  58320. // }}}
  58321. $ret = false;
  58322. $installphase = 'install';
  58323. $oldversion = false;
  58324. // {{{ Register that the package is installed -----------------------
  58325. if (empty($options['upgrade'])) {
  58326. // if 'force' is used, replace the info in registry
  58327. $usechannel = $channel;
  58328. if ($channel == 'pecl.php.net') {
  58329. $test = $installregistry->packageExists($pkgname, $channel);
  58330. if (!$test) {
  58331. $test = $installregistry->packageExists($pkgname, 'pear.php.net');
  58332. $usechannel = 'pear.php.net';
  58333. }
  58334. } else {
  58335. $test = $installregistry->packageExists($pkgname, $channel);
  58336. }
  58337. if (!empty($options['force']) && $test) {
  58338. $oldversion = $installregistry->packageInfo($pkgname, 'version', $usechannel);
  58339. $installregistry->deletePackage($pkgname, $usechannel);
  58340. }
  58341. $ret = $installregistry->addPackage2($pkg);
  58342. } else {
  58343. if ($dirtree) {
  58344. $this->startFileTransaction();
  58345. // attempt to delete empty directories
  58346. uksort($dirtree, array($this, '_sortDirs'));
  58347. foreach($dirtree as $dir => $notused) {
  58348. $this->addFileOperation('rmdir', array($dir));
  58349. }
  58350. $this->commitFileTransaction();
  58351. }
  58352. $usechannel = $channel;
  58353. if ($channel == 'pecl.php.net') {
  58354. $test = $installregistry->packageExists($pkgname, $channel);
  58355. if (!$test) {
  58356. $test = $installregistry->packageExists($pkgname, 'pear.php.net');
  58357. $usechannel = 'pear.php.net';
  58358. }
  58359. } else {
  58360. $test = $installregistry->packageExists($pkgname, $channel);
  58361. }
  58362. // new: upgrade installs a package if it isn't installed
  58363. if (!$test) {
  58364. $ret = $installregistry->addPackage2($pkg);
  58365. } else {
  58366. if ($usechannel != $channel) {
  58367. $installregistry->deletePackage($pkgname, $usechannel);
  58368. $ret = $installregistry->addPackage2($pkg);
  58369. } else {
  58370. $ret = $installregistry->updatePackage2($pkg);
  58371. }
  58372. $installphase = 'upgrade';
  58373. }
  58374. }
  58375. if (!$ret) {
  58376. $this->configSet('default_channel', $savechannel);
  58377. return $this->raiseError("Adding package $channel/$pkgname to registry failed");
  58378. }
  58379. // }}}
  58380. $this->configSet('default_channel', $savechannel);
  58381. if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist
  58382. if (PEAR_Task_Common::hasPostinstallTasks()) {
  58383. PEAR_Task_Common::runPostinstallTasks($installphase);
  58384. }
  58385. }
  58386. return $pkg->toArray(true);
  58387. }
  58388. // }}}
  58389. // {{{ _compileSourceFiles()
  58390. /**
  58391. * @param string
  58392. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  58393. * @param mixed[] $configureoptions
  58394. */
  58395. function _compileSourceFiles($savechannel, &$filelist, $configureoptions)
  58396. {
  58397. require_once 'PEAR/Builder.php';
  58398. $this->log(1, "$this->source_files source files, building");
  58399. $bob = new PEAR_Builder($configureoptions, $this->ui);
  58400. $bob->debug = $this->debug;
  58401. $built = $bob->build($filelist, array(&$this, '_buildCallback'));
  58402. if (PEAR::isError($built)) {
  58403. $this->rollbackFileTransaction();
  58404. $this->configSet('default_channel', $savechannel);
  58405. return $built;
  58406. }
  58407. $this->log(1, "\nBuild process completed successfully");
  58408. foreach ($built as $ext) {
  58409. $bn = basename($ext['file']);
  58410. list($_ext_name, $_ext_suff) = explode('.', $bn);
  58411. if ($_ext_suff == 'so' || $_ext_suff == 'dll') {
  58412. if (extension_loaded($_ext_name)) {
  58413. return $this->raiseError("Extension '$_ext_name' already loaded. " .
  58414. 'Please unload it in your php.ini file ' .
  58415. 'prior to install or upgrade');
  58416. }
  58417. $role = 'ext';
  58418. } else {
  58419. $role = 'src';
  58420. }
  58421. $dest = $ext['dest'];
  58422. $packagingroot = '';
  58423. if (isset($this->_options['packagingroot'])) {
  58424. $packagingroot = $this->_options['packagingroot'];
  58425. }
  58426. $copyto = $this->_prependPath($dest, $packagingroot);
  58427. $extra = $copyto != $dest ? " as '$copyto'" : '';
  58428. $this->log(1, "Installing '$dest'$extra");
  58429. $copydir = dirname($copyto);
  58430. // pretty much nothing happens if we are only registering the install
  58431. if (empty($this->_options['register-only'])) {
  58432. if (!file_exists($copydir) || !is_dir($copydir)) {
  58433. if (!$this->mkDirHier($copydir)) {
  58434. return $this->raiseError("failed to mkdir $copydir",
  58435. PEAR_INSTALLER_FAILED);
  58436. }
  58437. $this->log(3, "+ mkdir $copydir");
  58438. }
  58439. if (!@copy($ext['file'], $copyto)) {
  58440. return $this->raiseError(
  58441. "failed to write $copyto (" . error_get_last()["message"] . ")",
  58442. PEAR_INSTALLER_FAILED);
  58443. }
  58444. $this->log(3, "+ cp $ext[file] $copyto");
  58445. $this->addFileOperation('rename', array($ext['file'], $copyto));
  58446. if (!OS_WINDOWS) {
  58447. $mode = 0666 & ~(int)octdec($this->config->get('umask'));
  58448. $this->addFileOperation('chmod', array($mode, $copyto));
  58449. if (!@chmod($copyto, $mode)) {
  58450. $this->log(0, "failed to change mode of $copyto (" .
  58451. error_get_last()["message"] . ")");
  58452. }
  58453. }
  58454. }
  58455. $data = array(
  58456. 'role' => $role,
  58457. 'name' => $bn,
  58458. 'installed_as' => $dest,
  58459. 'php_api' => $ext['php_api'],
  58460. 'zend_mod_api' => $ext['zend_mod_api'],
  58461. 'zend_ext_api' => $ext['zend_ext_api'],
  58462. );
  58463. if ($filelist->getPackageXmlVersion() == '1.0') {
  58464. $filelist->installedFile($bn, $data);
  58465. } else {
  58466. $filelist->installedFile($bn, array('attribs' => $data));
  58467. }
  58468. }
  58469. }
  58470. // }}}
  58471. function &getUninstallPackages()
  58472. {
  58473. return $this->_downloadedPackages;
  58474. }
  58475. // {{{ uninstall()
  58476. /**
  58477. * Uninstall a package
  58478. *
  58479. * This method removes all files installed by the application, and then
  58480. * removes any empty directories.
  58481. * @param string package name
  58482. * @param array Command-line options. Possibilities include:
  58483. *
  58484. * - installroot: base installation dir, if not the default
  58485. * - register-only : update registry but don't remove files
  58486. * - nodeps: do not process dependencies of other packages to ensure
  58487. * uninstallation does not break things
  58488. */
  58489. function uninstall($package, $options = array())
  58490. {
  58491. $installRoot = isset($options['installroot']) ? $options['installroot'] : '';
  58492. $this->config->setInstallRoot($installRoot);
  58493. $this->installroot = '';
  58494. $this->_registry = &$this->config->getRegistry();
  58495. if (is_object($package)) {
  58496. $channel = $package->getChannel();
  58497. $pkg = $package;
  58498. $package = $pkg->getPackage();
  58499. } else {
  58500. $pkg = false;
  58501. $info = $this->_registry->parsePackageName($package,
  58502. $this->config->get('default_channel'));
  58503. $channel = $info['channel'];
  58504. $package = $info['package'];
  58505. }
  58506. $savechannel = $this->config->get('default_channel');
  58507. $this->configSet('default_channel', $channel);
  58508. if (!is_object($pkg)) {
  58509. $pkg = $this->_registry->getPackage($package, $channel);
  58510. }
  58511. if (!$pkg) {
  58512. $this->configSet('default_channel', $savechannel);
  58513. return $this->raiseError($this->_registry->parsedPackageNameToString(
  58514. array(
  58515. 'channel' => $channel,
  58516. 'package' => $package
  58517. ), true) . ' not installed');
  58518. }
  58519. if ($pkg->getInstalledBinary()) {
  58520. // this is just an alias for a binary package
  58521. return $this->_registry->deletePackage($package, $channel);
  58522. }
  58523. $filelist = $pkg->getFilelist();
  58524. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  58525. if (!class_exists('PEAR_Dependency2')) {
  58526. require_once 'PEAR/Dependency2.php';
  58527. }
  58528. $depchecker = new PEAR_Dependency2($this->config, $options,
  58529. array('channel' => $channel, 'package' => $package),
  58530. PEAR_VALIDATE_UNINSTALLING);
  58531. $e = $depchecker->validatePackageUninstall($this);
  58532. PEAR::staticPopErrorHandling();
  58533. if (PEAR::isError($e)) {
  58534. if (!isset($options['ignore-errors'])) {
  58535. return $this->raiseError($e);
  58536. }
  58537. if (!isset($options['soft'])) {
  58538. $this->log(0, 'WARNING: ' . $e->getMessage());
  58539. }
  58540. } elseif (is_array($e)) {
  58541. if (!isset($options['soft'])) {
  58542. $this->log(0, $e[0]);
  58543. }
  58544. }
  58545. $this->pkginfo = &$pkg;
  58546. // pretty much nothing happens if we are only registering the uninstall
  58547. if (empty($options['register-only'])) {
  58548. // {{{ Delete the files
  58549. $this->startFileTransaction();
  58550. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  58551. if (PEAR::isError($err = $this->_deletePackageFiles($package, $channel))) {
  58552. PEAR::popErrorHandling();
  58553. $this->rollbackFileTransaction();
  58554. $this->configSet('default_channel', $savechannel);
  58555. if (!isset($options['ignore-errors'])) {
  58556. return $this->raiseError($err);
  58557. }
  58558. if (!isset($options['soft'])) {
  58559. $this->log(0, 'WARNING: ' . $err->getMessage());
  58560. }
  58561. } else {
  58562. PEAR::popErrorHandling();
  58563. }
  58564. if (!$this->commitFileTransaction()) {
  58565. $this->rollbackFileTransaction();
  58566. if (!isset($options['ignore-errors'])) {
  58567. return $this->raiseError("uninstall failed");
  58568. }
  58569. if (!isset($options['soft'])) {
  58570. $this->log(0, 'WARNING: uninstall failed');
  58571. }
  58572. } else {
  58573. $this->startFileTransaction();
  58574. $dirtree = $pkg->getDirTree();
  58575. if ($dirtree === false) {
  58576. $this->configSet('default_channel', $savechannel);
  58577. return $this->_registry->deletePackage($package, $channel);
  58578. }
  58579. // attempt to delete empty directories
  58580. uksort($dirtree, array($this, '_sortDirs'));
  58581. foreach($dirtree as $dir => $notused) {
  58582. $this->addFileOperation('rmdir', array($dir));
  58583. }
  58584. if (!$this->commitFileTransaction()) {
  58585. $this->rollbackFileTransaction();
  58586. if (!isset($options['ignore-errors'])) {
  58587. return $this->raiseError("uninstall failed");
  58588. }
  58589. if (!isset($options['soft'])) {
  58590. $this->log(0, 'WARNING: uninstall failed');
  58591. }
  58592. }
  58593. }
  58594. // }}}
  58595. }
  58596. $this->configSet('default_channel', $savechannel);
  58597. // Register that the package is no longer installed
  58598. return $this->_registry->deletePackage($package, $channel);
  58599. }
  58600. /**
  58601. * Sort a list of arrays of array(downloaded packagefilename) by dependency.
  58602. *
  58603. * It also removes duplicate dependencies
  58604. * @param array an array of PEAR_PackageFile_v[1/2] objects
  58605. * @return array|PEAR_Error array of array(packagefilename, package.xml contents)
  58606. */
  58607. function sortPackagesForUninstall(&$packages)
  58608. {
  58609. $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->config);
  58610. if (PEAR::isError($this->_dependencyDB)) {
  58611. return $this->_dependencyDB;
  58612. }
  58613. usort($packages, array(&$this, '_sortUninstall'));
  58614. }
  58615. function _sortUninstall($a, $b)
  58616. {
  58617. if (!$a->getDeps() && !$b->getDeps()) {
  58618. return 0; // neither package has dependencies, order is insignificant
  58619. }
  58620. if ($a->getDeps() && !$b->getDeps()) {
  58621. return -1; // $a must be installed after $b because $a has dependencies
  58622. }
  58623. if (!$a->getDeps() && $b->getDeps()) {
  58624. return 1; // $b must be installed after $a because $b has dependencies
  58625. }
  58626. // both packages have dependencies
  58627. if ($this->_dependencyDB->dependsOn($a, $b)) {
  58628. return -1;
  58629. }
  58630. if ($this->_dependencyDB->dependsOn($b, $a)) {
  58631. return 1;
  58632. }
  58633. return 0;
  58634. }
  58635. // }}}
  58636. // {{{ _sortDirs()
  58637. function _sortDirs($a, $b)
  58638. {
  58639. if (strnatcmp($a, $b) == -1) return 1;
  58640. if (strnatcmp($a, $b) == 1) return -1;
  58641. return 0;
  58642. }
  58643. // }}}
  58644. // {{{ _buildCallback()
  58645. function _buildCallback($what, $data)
  58646. {
  58647. if (($what == 'cmdoutput' && $this->debug > 1) ||
  58648. ($what == 'output' && $this->debug > 0)) {
  58649. $this->ui->outputData(rtrim($data), 'build');
  58650. }
  58651. }
  58652. // }}}
  58653. }
  58654. �������������������������������������������������������PEAR-1.10.16/PEAR/PackageFile.php�������������������������������������������������������������������0000664�0001750�0001750�00000036746�14720722517�015703� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  58655. /**
  58656. * PEAR_PackageFile, package.xml parsing utility class
  58657. *
  58658. * PHP versions 4 and 5
  58659. *
  58660. * @category pear
  58661. * @package PEAR
  58662. * @author Greg Beaver <cellog@php.net>
  58663. * @copyright 1997-2009 The Authors
  58664. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  58665. * @link http://pear.php.net/package/PEAR
  58666. * @since File available since Release 1.4.0a1
  58667. */
  58668. /**
  58669. * needed for PEAR_VALIDATE_* constants
  58670. */
  58671. require_once 'PEAR/Validate.php';
  58672. /**
  58673. * Error code if the package.xml <package> tag does not contain a valid version
  58674. */
  58675. define('PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION', 1);
  58676. /**
  58677. * Error code if the package.xml <package> tag version is not supported (version 1.0 and 1.1 are the only supported versions,
  58678. * currently
  58679. */
  58680. define('PEAR_PACKAGEFILE_ERROR_INVALID_PACKAGEVERSION', 2);
  58681. /**
  58682. * Abstraction for the package.xml package description file
  58683. *
  58684. * @category pear
  58685. * @package PEAR
  58686. * @author Greg Beaver <cellog@php.net>
  58687. * @copyright 1997-2009 The Authors
  58688. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  58689. * @version Release: 1.10.16
  58690. * @link http://pear.php.net/package/PEAR
  58691. * @since Class available since Release 1.4.0a1
  58692. */
  58693. class PEAR_PackageFile
  58694. {
  58695. /**
  58696. * @var PEAR_Config
  58697. */
  58698. var $_config;
  58699. var $_debug;
  58700. var $_logger = false;
  58701. /**
  58702. * @var boolean
  58703. */
  58704. var $_rawReturn = false;
  58705. /**
  58706. * helper for extracting Archive_Tar errors
  58707. * @var array
  58708. * @access private
  58709. */
  58710. var $_extractErrors = array();
  58711. /**
  58712. *
  58713. * @param PEAR_Config $config
  58714. * @param ? $debug
  58715. * @param string @tmpdir Optional temporary directory for uncompressing
  58716. * files
  58717. */
  58718. function __construct(&$config, $debug = false)
  58719. {
  58720. $this->_config = $config;
  58721. $this->_debug = $debug;
  58722. }
  58723. /**
  58724. * Turn off validation - return a parsed package.xml without checking it
  58725. *
  58726. * This is used by the package-validate command
  58727. */
  58728. function rawReturn()
  58729. {
  58730. $this->_rawReturn = true;
  58731. }
  58732. function setLogger(&$l)
  58733. {
  58734. $this->_logger = &$l;
  58735. }
  58736. /**
  58737. * Create a PEAR_PackageFile_Parser_v* of a given version.
  58738. * @param int $version
  58739. * @return PEAR_PackageFile_Parser_v1|PEAR_PackageFile_Parser_v1
  58740. */
  58741. function &parserFactory($version)
  58742. {
  58743. if (!in_array($version[0], array('1', '2'))) {
  58744. $a = false;
  58745. return $a;
  58746. }
  58747. include_once 'PEAR/PackageFile/Parser/v' . $version[0] . '.php';
  58748. $version = $version[0];
  58749. $class = "PEAR_PackageFile_Parser_v$version";
  58750. $a = new $class;
  58751. return $a;
  58752. }
  58753. /**
  58754. * For simpler unit-testing
  58755. * @return string
  58756. */
  58757. function getClassPrefix()
  58758. {
  58759. return 'PEAR_PackageFile_v';
  58760. }
  58761. /**
  58762. * Create a PEAR_PackageFile_v* of a given version.
  58763. * @param int $version
  58764. * @return PEAR_PackageFile_v1|PEAR_PackageFile_v1
  58765. */
  58766. function &factory($version)
  58767. {
  58768. if (!in_array($version[0], array('1', '2'))) {
  58769. $a = false;
  58770. return $a;
  58771. }
  58772. include_once 'PEAR/PackageFile/v' . $version[0] . '.php';
  58773. $version = $version[0];
  58774. $class = $this->getClassPrefix() . $version;
  58775. $a = new $class;
  58776. return $a;
  58777. }
  58778. /**
  58779. * Create a PEAR_PackageFile_v* from its toArray() method
  58780. *
  58781. * WARNING: no validation is performed, the array is assumed to be valid,
  58782. * always parse from xml if you want validation.
  58783. * @param array $arr
  58784. * @return PEAR_PackageFileManager_v1|PEAR_PackageFileManager_v2
  58785. * @uses factory() to construct the returned object.
  58786. */
  58787. function &fromArray($arr)
  58788. {
  58789. if (isset($arr['xsdversion'])) {
  58790. $obj = &$this->factory($arr['xsdversion']);
  58791. if ($this->_logger) {
  58792. $obj->setLogger($this->_logger);
  58793. }
  58794. $obj->setConfig($this->_config);
  58795. $obj->fromArray($arr);
  58796. return $obj;
  58797. }
  58798. if (isset($arr['package']['attribs']['version'])) {
  58799. $obj = &$this->factory($arr['package']['attribs']['version']);
  58800. } else {
  58801. $obj = &$this->factory('1.0');
  58802. }
  58803. if ($this->_logger) {
  58804. $obj->setLogger($this->_logger);
  58805. }
  58806. $obj->setConfig($this->_config);
  58807. $obj->fromArray($arr);
  58808. return $obj;
  58809. }
  58810. /**
  58811. * Create a PEAR_PackageFile_v* from an XML string.
  58812. * @access public
  58813. * @param string $data contents of package.xml file
  58814. * @param int $state package state (one of PEAR_VALIDATE_* constants)
  58815. * @param string $file full path to the package.xml file (and the files
  58816. * it references)
  58817. * @param string $archive optional name of the archive that the XML was
  58818. * extracted from, if any
  58819. * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2
  58820. * @uses parserFactory() to construct a parser to load the package.
  58821. */
  58822. function &fromXmlString($data, $state, $file, $archive = false)
  58823. {
  58824. if (preg_match('/<package[^>]+version=[\'"]([0-9]+\.[0-9]+)[\'"]/', $data, $packageversion)) {
  58825. if (!in_array($packageversion[1], array('1.0', '2.0', '2.1'))) {
  58826. return PEAR::raiseError('package.xml version "' . $packageversion[1] .
  58827. '" is not supported, only 1.0, 2.0, and 2.1 are supported.');
  58828. }
  58829. $object = &$this->parserFactory($packageversion[1]);
  58830. if ($this->_logger) {
  58831. $object->setLogger($this->_logger);
  58832. }
  58833. $object->setConfig($this->_config);
  58834. $pf = $object->parse($data, $file, $archive);
  58835. if (PEAR::isError($pf)) {
  58836. return $pf;
  58837. }
  58838. if ($this->_rawReturn) {
  58839. return $pf;
  58840. }
  58841. if (!$pf->validate($state)) {;
  58842. if ($this->_config->get('verbose') > 0
  58843. && $this->_logger && $pf->getValidationWarnings(false)
  58844. ) {
  58845. foreach ($pf->getValidationWarnings(false) as $warning) {
  58846. $this->_logger->log(0, 'ERROR: ' . $warning['message']);
  58847. }
  58848. }
  58849. $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed',
  58850. 2, null, null, $pf->getValidationWarnings());
  58851. return $a;
  58852. }
  58853. if ($this->_logger && $pf->getValidationWarnings(false)) {
  58854. foreach ($pf->getValidationWarnings() as $warning) {
  58855. $this->_logger->log(0, 'WARNING: ' . $warning['message']);
  58856. }
  58857. }
  58858. if (method_exists($pf, 'flattenFilelist')) {
  58859. $pf->flattenFilelist(); // for v2
  58860. }
  58861. return $pf;
  58862. } elseif (preg_match('/<package[^>]+version=[\'"]([^"\']+)[\'"]/', $data, $packageversion)) {
  58863. $a = PEAR::raiseError('package.xml file "' . $file .
  58864. '" has unsupported package.xml <package> version "' . $packageversion[1] . '"');
  58865. return $a;
  58866. } else {
  58867. if (!class_exists('PEAR_ErrorStack')) {
  58868. require_once 'PEAR/ErrorStack.php';
  58869. }
  58870. PEAR_ErrorStack::staticPush('PEAR_PackageFile',
  58871. PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION,
  58872. 'warning', array('xml' => $data), 'package.xml "' . $file .
  58873. '" has no package.xml <package> version');
  58874. $object = &$this->parserFactory('1.0');
  58875. $object->setConfig($this->_config);
  58876. $pf = $object->parse($data, $file, $archive);
  58877. if (PEAR::isError($pf)) {
  58878. return $pf;
  58879. }
  58880. if ($this->_rawReturn) {
  58881. return $pf;
  58882. }
  58883. if (!$pf->validate($state)) {
  58884. $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed',
  58885. 2, null, null, $pf->getValidationWarnings());
  58886. return $a;
  58887. }
  58888. if ($this->_logger && $pf->getValidationWarnings(false)) {
  58889. foreach ($pf->getValidationWarnings() as $warning) {
  58890. $this->_logger->log(0, 'WARNING: ' . $warning['message']);
  58891. }
  58892. }
  58893. if (method_exists($pf, 'flattenFilelist')) {
  58894. $pf->flattenFilelist(); // for v2
  58895. }
  58896. return $pf;
  58897. }
  58898. }
  58899. /**
  58900. * Register a temporary file or directory. When the destructor is
  58901. * executed, all registered temporary files and directories are
  58902. * removed.
  58903. *
  58904. * @param string $file name of file or directory
  58905. * @return void
  58906. */
  58907. static function addTempFile($file)
  58908. {
  58909. $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
  58910. }
  58911. /**
  58912. * Create a PEAR_PackageFile_v* from a compressed Tar or Tgz file.
  58913. * @access public
  58914. * @param string contents of package.xml file
  58915. * @param int package state (one of PEAR_VALIDATE_* constants)
  58916. * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2
  58917. * @using Archive_Tar to extract the files
  58918. * @using fromPackageFile() to load the package after the package.xml
  58919. * file is extracted.
  58920. */
  58921. function &fromTgzFile($file, $state)
  58922. {
  58923. if (!class_exists('Archive_Tar')) {
  58924. require_once 'Archive/Tar.php';
  58925. }
  58926. $tar = new Archive_Tar($file);
  58927. if ($this->_debug <= 1) {
  58928. $tar->pushErrorHandling(PEAR_ERROR_RETURN);
  58929. }
  58930. $content = $tar->listContent();
  58931. if ($this->_debug <= 1) {
  58932. $tar->popErrorHandling();
  58933. }
  58934. if (!is_array($content)) {
  58935. if (is_string($file) && strlen($file) < 255 &&
  58936. (!file_exists($file) || !@is_file($file))) {
  58937. $ret = PEAR::raiseError("could not open file \"$file\"");
  58938. return $ret;
  58939. }
  58940. $file = realpath($file);
  58941. $ret = PEAR::raiseError("Could not get contents of package \"$file\"".
  58942. '. Invalid tgz file.');
  58943. return $ret;
  58944. }
  58945. if (!count($content) && !@is_file($file)) {
  58946. $ret = PEAR::raiseError("could not open file \"$file\"");
  58947. return $ret;
  58948. }
  58949. $xml = null;
  58950. $origfile = $file;
  58951. foreach ($content as $file) {
  58952. $name = $file['filename'];
  58953. if ($name == 'package2.xml') { // allow a .tgz to distribute both versions
  58954. $xml = $name;
  58955. break;
  58956. }
  58957. if ($name == 'package.xml') {
  58958. $xml = $name;
  58959. break;
  58960. } elseif (preg_match('/package.xml$/', $name, $match)) {
  58961. $xml = $name;
  58962. break;
  58963. }
  58964. }
  58965. $tmpdir = System::mktemp('-t "' . $this->_config->get('temp_dir') . '" -d pear');
  58966. if ($tmpdir === false) {
  58967. $ret = PEAR::raiseError("there was a problem with getting the configured temp directory");
  58968. return $ret;
  58969. }
  58970. PEAR_PackageFile::addTempFile($tmpdir);
  58971. $this->_extractErrors();
  58972. PEAR::staticPushErrorHandling(PEAR_ERROR_CALLBACK, array($this, '_extractErrors'));
  58973. if (!$xml || !$tar->extractList(array($xml), $tmpdir)) {
  58974. $extra = implode("\n", $this->_extractErrors());
  58975. if ($extra) {
  58976. $extra = ' ' . $extra;
  58977. }
  58978. PEAR::staticPopErrorHandling();
  58979. $ret = PEAR::raiseError('could not extract the package.xml file from "' .
  58980. $origfile . '"' . $extra);
  58981. return $ret;
  58982. }
  58983. PEAR::staticPopErrorHandling();
  58984. $ret = &PEAR_PackageFile::fromPackageFile("$tmpdir/$xml", $state, $origfile);
  58985. return $ret;
  58986. }
  58987. /**
  58988. * helper callback for extracting Archive_Tar errors
  58989. *
  58990. * @param PEAR_Error|null $err
  58991. * @return array
  58992. * @access private
  58993. */
  58994. function _extractErrors($err = null)
  58995. {
  58996. static $errors = array();
  58997. if ($err === null) {
  58998. $e = $errors;
  58999. $errors = array();
  59000. return $e;
  59001. }
  59002. $errors[] = $err->getMessage();
  59003. }
  59004. /**
  59005. * Create a PEAR_PackageFile_v* from a package.xml file.
  59006. *
  59007. * @access public
  59008. * @param string $descfile name of package xml file
  59009. * @param int $state package state (one of PEAR_VALIDATE_* constants)
  59010. * @param string|false $archive name of the archive this package.xml came
  59011. * from, if any
  59012. * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2
  59013. * @uses PEAR_PackageFile::fromXmlString to create the oject after the
  59014. * XML is loaded from the package.xml file.
  59015. */
  59016. function &fromPackageFile($descfile, $state, $archive = false)
  59017. {
  59018. $fp = false;
  59019. if (is_string($descfile) && strlen($descfile) < 255 &&
  59020. (
  59021. !file_exists($descfile) || !is_file($descfile) || !is_readable($descfile)
  59022. || (!$fp = @fopen($descfile, 'r'))
  59023. )
  59024. ) {
  59025. $a = PEAR::raiseError("Unable to open $descfile");
  59026. return $a;
  59027. }
  59028. // read the whole thing so we only get one cdata callback
  59029. // for each block of cdata
  59030. fclose($fp);
  59031. $data = file_get_contents($descfile);
  59032. $ret = &PEAR_PackageFile::fromXmlString($data, $state, $descfile, $archive);
  59033. return $ret;
  59034. }
  59035. /**
  59036. * Create a PEAR_PackageFile_v* from a .tgz archive or package.xml file.
  59037. *
  59038. * This method is able to extract information about a package from a .tgz
  59039. * archive or from a XML package definition file.
  59040. *
  59041. * @access public
  59042. * @param string $info file name
  59043. * @param int $state package state (one of PEAR_VALIDATE_* constants)
  59044. * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2
  59045. * @uses fromPackageFile() if the file appears to be XML
  59046. * @uses fromTgzFile() to load all non-XML files
  59047. */
  59048. function &fromAnyFile($info, $state)
  59049. {
  59050. if (is_dir($info)) {
  59051. $dir_name = realpath($info);
  59052. if (file_exists($dir_name . '/package.xml')) {
  59053. $info = PEAR_PackageFile::fromPackageFile($dir_name . '/package.xml', $state);
  59054. } elseif (file_exists($dir_name . '/package2.xml')) {
  59055. $info = PEAR_PackageFile::fromPackageFile($dir_name . '/package2.xml', $state);
  59056. } else {
  59057. $info = PEAR::raiseError("No package definition found in '$info' directory");
  59058. }
  59059. return $info;
  59060. }
  59061. $fp = false;
  59062. if (is_string($info) && strlen($info) < 255 &&
  59063. (file_exists($info) || ($fp = @fopen($info, 'r')))
  59064. ) {
  59065. if ($fp) {
  59066. fclose($fp);
  59067. }
  59068. $tmp = substr($info, -4);
  59069. if ($tmp == '.xml') {
  59070. $info = &PEAR_PackageFile::fromPackageFile($info, $state);
  59071. } elseif ($tmp == '.tar' || $tmp == '.tgz') {
  59072. $info = &PEAR_PackageFile::fromTgzFile($info, $state);
  59073. } else {
  59074. $fp = fopen($info, 'r');
  59075. $test = fread($fp, 5);
  59076. fclose($fp);
  59077. if ($test == '<?xml') {
  59078. $info = &PEAR_PackageFile::fromPackageFile($info, $state);
  59079. } else {
  59080. $info = &PEAR_PackageFile::fromTgzFile($info, $state);
  59081. }
  59082. }
  59083. return $info;
  59084. }
  59085. $info = PEAR::raiseError("Cannot open '$info' for parsing");
  59086. return $info;
  59087. }
  59088. }
  59089. ��������������������������PEAR-1.10.16/PEAR/Packager.php����������������������������������������������������������������������0000644�0001750�0001750�00000017040�14720722517�015245� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  59090. /**
  59091. * PEAR_Packager for generating releases
  59092. *
  59093. * PHP versions 4 and 5
  59094. *
  59095. * @category pear
  59096. * @package PEAR
  59097. * @author Stig Bakken <ssb@php.net>
  59098. * @author Tomas V. V. Cox <cox@idecnet.com>
  59099. * @author Greg Beaver <cellog@php.net>
  59100. * @copyright 1997-2009 The Authors
  59101. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  59102. * @link http://pear.php.net/package/PEAR
  59103. * @since File available since Release 0.1
  59104. */
  59105. /**
  59106. * base class
  59107. */
  59108. require_once 'PEAR/Common.php';
  59109. require_once 'PEAR/PackageFile.php';
  59110. require_once 'System.php';
  59111. /**
  59112. * Administration class used to make a PEAR release tarball.
  59113. *
  59114. * @category pear
  59115. * @package PEAR
  59116. * @author Greg Beaver <cellog@php.net>
  59117. * @copyright 1997-2009 The Authors
  59118. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  59119. * @version Release: 1.10.16
  59120. * @link http://pear.php.net/package/PEAR
  59121. * @since Class available since Release 0.1
  59122. */
  59123. class PEAR_Packager extends PEAR_Common
  59124. {
  59125. /**
  59126. * @var PEAR_Registry
  59127. */
  59128. var $_registry;
  59129. function package($pkgfile = null, $compress = true, $pkg2 = null)
  59130. {
  59131. // {{{ validate supplied package.xml file
  59132. if (empty($pkgfile)) {
  59133. $pkgfile = 'package.xml';
  59134. }
  59135. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  59136. $pkg = new PEAR_PackageFile($this->config, $this->debug);
  59137. $pf = &$pkg->fromPackageFile($pkgfile, PEAR_VALIDATE_NORMAL);
  59138. $main = &$pf;
  59139. PEAR::staticPopErrorHandling();
  59140. if (PEAR::isError($pf)) {
  59141. if (is_array($pf->getUserInfo())) {
  59142. foreach ($pf->getUserInfo() as $error) {
  59143. $this->log(0, 'Error: ' . $error['message']);
  59144. }
  59145. }
  59146. $this->log(0, $pf->getMessage());
  59147. return $this->raiseError("Cannot package, errors in package file");
  59148. }
  59149. foreach ($pf->getValidationWarnings() as $warning) {
  59150. $this->log(1, 'Warning: ' . $warning['message']);
  59151. }
  59152. // }}}
  59153. if ($pkg2) {
  59154. $this->log(0, 'Attempting to process the second package file');
  59155. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  59156. $pf2 = &$pkg->fromPackageFile($pkg2, PEAR_VALIDATE_NORMAL);
  59157. PEAR::staticPopErrorHandling();
  59158. if (PEAR::isError($pf2)) {
  59159. if (is_array($pf2->getUserInfo())) {
  59160. foreach ($pf2->getUserInfo() as $error) {
  59161. $this->log(0, 'Error: ' . $error['message']);
  59162. }
  59163. }
  59164. $this->log(0, $pf2->getMessage());
  59165. return $this->raiseError("Cannot package, errors in second package file");
  59166. }
  59167. foreach ($pf2->getValidationWarnings() as $warning) {
  59168. $this->log(1, 'Warning: ' . $warning['message']);
  59169. }
  59170. if ($pf2->getPackagexmlVersion() == '2.0' ||
  59171. $pf2->getPackagexmlVersion() == '2.1'
  59172. ) {
  59173. $main = &$pf2;
  59174. $other = &$pf;
  59175. } else {
  59176. $main = &$pf;
  59177. $other = &$pf2;
  59178. }
  59179. if ($main->getPackagexmlVersion() != '2.0' &&
  59180. $main->getPackagexmlVersion() != '2.1') {
  59181. return PEAR::raiseError('Error: cannot package two package.xml version 1.0, can ' .
  59182. 'only package together a package.xml 1.0 and package.xml 2.0');
  59183. }
  59184. if ($other->getPackagexmlVersion() != '1.0') {
  59185. return PEAR::raiseError('Error: cannot package two package.xml version 2.0, can ' .
  59186. 'only package together a package.xml 1.0 and package.xml 2.0');
  59187. }
  59188. }
  59189. $main->setLogger($this);
  59190. if (!$main->validate(PEAR_VALIDATE_PACKAGING)) {
  59191. foreach ($main->getValidationWarnings() as $warning) {
  59192. $this->log(0, 'Error: ' . $warning['message']);
  59193. }
  59194. return $this->raiseError("Cannot package, errors in package");
  59195. }
  59196. foreach ($main->getValidationWarnings() as $warning) {
  59197. $this->log(1, 'Warning: ' . $warning['message']);
  59198. }
  59199. if ($pkg2) {
  59200. $other->setLogger($this);
  59201. $a = false;
  59202. if (!$other->validate(PEAR_VALIDATE_NORMAL) || $a = !$main->isEquivalent($other)) {
  59203. foreach ($other->getValidationWarnings() as $warning) {
  59204. $this->log(0, 'Error: ' . $warning['message']);
  59205. }
  59206. foreach ($main->getValidationWarnings() as $warning) {
  59207. $this->log(0, 'Error: ' . $warning['message']);
  59208. }
  59209. if ($a) {
  59210. return $this->raiseError('The two package.xml files are not equivalent!');
  59211. }
  59212. return $this->raiseError("Cannot package, errors in package");
  59213. }
  59214. foreach ($other->getValidationWarnings() as $warning) {
  59215. $this->log(1, 'Warning: ' . $warning['message']);
  59216. }
  59217. $gen = &$main->getDefaultGenerator();
  59218. $tgzfile = $gen->toTgz2($this, $other, $compress);
  59219. if (PEAR::isError($tgzfile)) {
  59220. return $tgzfile;
  59221. }
  59222. $dest_package = basename($tgzfile);
  59223. $pkgdir = dirname($pkgfile);
  59224. // TAR the Package -------------------------------------------
  59225. $this->log(1, "Package $dest_package done");
  59226. if (file_exists("$pkgdir/CVS/Root")) {
  59227. $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pf->getVersion());
  59228. $cvstag = "RELEASE_$cvsversion";
  59229. $this->log(1, 'Tag the released code with "pear cvstag ' .
  59230. $main->getPackageFile() . '"');
  59231. $this->log(1, "(or set the CVS tag $cvstag by hand)");
  59232. } elseif (file_exists("$pkgdir/.svn")) {
  59233. $svnversion = preg_replace('/[^a-z0-9]/i', '.', $pf->getVersion());
  59234. $svntag = $pf->getName() . "-$svnversion";
  59235. $this->log(1, 'Tag the released code with "pear svntag ' .
  59236. $main->getPackageFile() . '"');
  59237. $this->log(1, "(or set the SVN tag $svntag by hand)");
  59238. }
  59239. } else { // this branch is executed for single packagefile packaging
  59240. $gen = &$pf->getDefaultGenerator();
  59241. $tgzfile = $gen->toTgz($this, $compress);
  59242. if (PEAR::isError($tgzfile)) {
  59243. $this->log(0, $tgzfile->getMessage());
  59244. return $this->raiseError("Cannot package, errors in package");
  59245. }
  59246. $dest_package = basename($tgzfile);
  59247. $pkgdir = dirname($pkgfile);
  59248. // TAR the Package -------------------------------------------
  59249. $this->log(1, "Package $dest_package done");
  59250. if (file_exists("$pkgdir/CVS/Root")) {
  59251. $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pf->getVersion());
  59252. $cvstag = "RELEASE_$cvsversion";
  59253. $this->log(1, "Tag the released code with `pear cvstag $pkgfile'");
  59254. $this->log(1, "(or set the CVS tag $cvstag by hand)");
  59255. } elseif (file_exists("$pkgdir/.svn")) {
  59256. $svnversion = preg_replace('/[^a-z0-9]/i', '.', $pf->getVersion());
  59257. $svntag = $pf->getName() . "-$svnversion";
  59258. $this->log(1, "Tag the released code with `pear svntag $pkgfile'");
  59259. $this->log(1, "(or set the SVN tag $svntag by hand)");
  59260. }
  59261. }
  59262. return $dest_package;
  59263. }
  59264. }������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Proxy.php�������������������������������������������������������������������������0000664�0001750�0001750�00000012752�14720722517�014660� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  59265. /**
  59266. * PEAR_Proxy
  59267. *
  59268. * HTTP Proxy handling
  59269. *
  59270. * @category pear
  59271. * @package PEAR
  59272. * @author Nico Boehr
  59273. * @copyright 1997-2009 The Authors
  59274. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  59275. * @link http://pear.php.net/package/PEAR
  59276. */
  59277. class PEAR_Proxy
  59278. {
  59279. var $config = null;
  59280. /**
  59281. * @access private
  59282. */
  59283. var $proxy_host;
  59284. /**
  59285. * @access private
  59286. */
  59287. var $proxy_port;
  59288. /**
  59289. * @access private
  59290. */
  59291. var $proxy_user;
  59292. /**
  59293. * @access private
  59294. */
  59295. var $proxy_pass;
  59296. /**
  59297. * @access private
  59298. */
  59299. var $proxy_schema;
  59300. function __construct($config = null)
  59301. {
  59302. $this->config = $config;
  59303. $this->_parseProxyInfo();
  59304. }
  59305. /**
  59306. * @access private
  59307. */
  59308. function _parseProxyInfo()
  59309. {
  59310. $this->proxy_host = $this->proxy_port = $this->proxy_user = $this->proxy_pass = '';
  59311. if ($this->config->get('http_proxy')&&
  59312. $proxy = parse_url($this->config->get('http_proxy'))
  59313. ) {
  59314. $this->proxy_host = isset($proxy['host']) ? $proxy['host'] : null;
  59315. $this->proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080;
  59316. $this->proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null;
  59317. $this->proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;
  59318. $this->proxy_schema = (isset($proxy['scheme']) && $proxy['scheme'] == 'https') ? 'https' : 'http';
  59319. }
  59320. }
  59321. /**
  59322. * @access private
  59323. */
  59324. function _httpConnect($fp, $host, $port)
  59325. {
  59326. fwrite($fp, "CONNECT $host:$port HTTP/1.1\r\n");
  59327. fwrite($fp, "Host: $host:$port\r\n");
  59328. if ($this->getProxyAuth()) {
  59329. fwrite($fp, 'Proxy-Authorization: Basic ' . $this->getProxyAuth() . "\r\n");
  59330. }
  59331. fwrite($fp, "\r\n");
  59332. while ($line = trim(fgets($fp, 1024))) {
  59333. if (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
  59334. $code = (int)$matches[1];
  59335. /* as per RFC 2817 */
  59336. if ($code < 200 || $code >= 300) {
  59337. return PEAR::raiseError("Establishing a CONNECT tunnel through proxy failed with response code $code");
  59338. }
  59339. }
  59340. }
  59341. // connection was successful -- establish SSL through
  59342. // the tunnel
  59343. $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
  59344. if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
  59345. $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
  59346. $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
  59347. }
  59348. // set the correct hostname for working hostname
  59349. // verification
  59350. stream_context_set_option($fp, 'ssl', 'peer_name', $host);
  59351. // blocking socket needed for
  59352. // stream_socket_enable_crypto()
  59353. // see
  59354. // <http://php.net/manual/en/function.stream-socket-enable-crypto.php>
  59355. stream_set_blocking ($fp, true);
  59356. $crypto_res = stream_socket_enable_crypto($fp, true, $crypto_method);
  59357. if (!$crypto_res) {
  59358. return PEAR::raiseError("Could not establish SSL connection through proxy: $crypto_res");
  59359. }
  59360. return true;
  59361. }
  59362. /**
  59363. * get the authorization information for the proxy, encoded to be
  59364. * passed in the Proxy-Authentication HTTP header.
  59365. * @return null|string the encoded authentication information if a
  59366. * proxy and authentication is configured, null
  59367. * otherwise.
  59368. */
  59369. function getProxyAuth()
  59370. {
  59371. if ($this->isProxyConfigured() && $this->proxy_user != '') {
  59372. return base64_encode($this->proxy_user . ':' . $this->proxy_pass);
  59373. }
  59374. return null;
  59375. }
  59376. function getProxyUser()
  59377. {
  59378. return $this->proxy_user;
  59379. }
  59380. /**
  59381. * Check if we are configured to use a proxy.
  59382. *
  59383. * @return boolean true if we are configured to use a proxy, false
  59384. * otherwise.
  59385. * @access public
  59386. */
  59387. function isProxyConfigured()
  59388. {
  59389. return $this->proxy_host != '';
  59390. }
  59391. /**
  59392. * Open a socket to a remote server, possibly involving a HTTP
  59393. * proxy.
  59394. *
  59395. * If an HTTP proxy has been configured (http_proxy PEAR_Config
  59396. * setting), the proxy will be used.
  59397. *
  59398. * @param string $host the host to connect to
  59399. * @param string $port the port to connect to
  59400. * @param boolean $secure if true, establish a secure connection
  59401. * using TLS.
  59402. * @access public
  59403. */
  59404. function openSocket($host, $port, $secure = false)
  59405. {
  59406. if ($this->isProxyConfigured()) {
  59407. $fp = @fsockopen(
  59408. $this->proxy_host, $this->proxy_port,
  59409. $errno, $errstr, 15
  59410. );
  59411. if (!$fp) {
  59412. return PEAR::raiseError("Connection to the proxy failed: $errstr", -9276);
  59413. }
  59414. /* HTTPS is to be used and we have a proxy, use CONNECT verb */
  59415. if ($secure) {
  59416. $res = $this->_httpConnect($fp, $host, $port);
  59417. if (PEAR::isError($res)) {
  59418. return $res;
  59419. }
  59420. }
  59421. } else {
  59422. if ($secure) {
  59423. $host = 'ssl://' . $host;
  59424. }
  59425. $fp = @fsockopen($host, $port, $errno, $errstr);
  59426. if (!$fp) {
  59427. return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
  59428. }
  59429. }
  59430. return $fp;
  59431. }
  59432. }
  59433. ����������������������PEAR-1.10.16/PEAR/Registry.php����������������������������������������������������������������������0000664�0001750�0001750�00000224270�14720722517�015347� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  59434. /**
  59435. * PEAR_Registry
  59436. *
  59437. * PHP versions 4 and 5
  59438. *
  59439. * @category pear
  59440. * @package PEAR
  59441. * @author Stig Bakken <ssb@php.net>
  59442. * @author Tomas V. V. Cox <cox@idecnet.com>
  59443. * @author Greg Beaver <cellog@php.net>
  59444. * @copyright 1997-2009 The Authors
  59445. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  59446. * @link http://pear.php.net/package/PEAR
  59447. * @since File available since Release 0.1
  59448. */
  59449. /**
  59450. * for PEAR_Error
  59451. */
  59452. require_once 'PEAR.php';
  59453. require_once 'PEAR/DependencyDB.php';
  59454. define('PEAR_REGISTRY_ERROR_LOCK', -2);
  59455. define('PEAR_REGISTRY_ERROR_FORMAT', -3);
  59456. define('PEAR_REGISTRY_ERROR_FILE', -4);
  59457. define('PEAR_REGISTRY_ERROR_CONFLICT', -5);
  59458. define('PEAR_REGISTRY_ERROR_CHANNEL_FILE', -6);
  59459. /**
  59460. * Administration class used to maintain the installed package database.
  59461. * @category pear
  59462. * @package PEAR
  59463. * @author Stig Bakken <ssb@php.net>
  59464. * @author Tomas V. V. Cox <cox@idecnet.com>
  59465. * @author Greg Beaver <cellog@php.net>
  59466. * @copyright 1997-2009 The Authors
  59467. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  59468. * @version Release: 1.10.16
  59469. * @link http://pear.php.net/package/PEAR
  59470. * @since Class available since Release 1.4.0a1
  59471. */
  59472. class PEAR_Registry extends PEAR
  59473. {
  59474. /**
  59475. * File containing all channel information.
  59476. * @var string
  59477. */
  59478. var $channels = '';
  59479. /** Directory where registry files are stored.
  59480. * @var string
  59481. */
  59482. var $statedir = '';
  59483. /** File where the file map is stored
  59484. * @var string
  59485. */
  59486. var $filemap = '';
  59487. /** Directory where registry files for channels are stored.
  59488. * @var string
  59489. */
  59490. var $channelsdir = '';
  59491. /** Name of file used for locking the registry
  59492. * @var string
  59493. */
  59494. var $lockfile = '';
  59495. /** File descriptor used during locking
  59496. * @var resource
  59497. */
  59498. var $lock_fp = null;
  59499. /** Mode used during locking
  59500. * @var int
  59501. */
  59502. var $lock_mode = 0; // XXX UNUSED
  59503. /** Cache of package information. Structure:
  59504. * array(
  59505. * 'package' => array('id' => ... ),
  59506. * ... )
  59507. * @var array
  59508. */
  59509. var $pkginfo_cache = array();
  59510. /** Cache of file map. Structure:
  59511. * array( '/path/to/file' => 'package', ... )
  59512. * @var array
  59513. */
  59514. var $filemap_cache = array();
  59515. /**
  59516. * @var false|PEAR_ChannelFile
  59517. */
  59518. var $_pearChannel;
  59519. /**
  59520. * @var false|PEAR_ChannelFile
  59521. */
  59522. var $_peclChannel;
  59523. /**
  59524. * @var false|PEAR_ChannelFile
  59525. */
  59526. var $_docChannel;
  59527. /**
  59528. * @var PEAR_DependencyDB
  59529. */
  59530. var $_dependencyDB;
  59531. /**
  59532. * @var PEAR_Config
  59533. */
  59534. var $_config;
  59535. /**
  59536. * PEAR_Registry constructor.
  59537. *
  59538. * @param string (optional) PEAR install directory (for .php files)
  59539. * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PEAR channel, if
  59540. * default values are not desired. Only used the very first time a PEAR
  59541. * repository is initialized
  59542. * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PECL channel, if
  59543. * default values are not desired. Only used the very first time a PEAR
  59544. * repository is initialized
  59545. *
  59546. * @access public
  59547. */
  59548. function __construct($pear_install_dir = PEAR_INSTALL_DIR, $pear_channel = false,
  59549. $pecl_channel = false, $pear_metadata_dir = '')
  59550. {
  59551. parent::__construct();
  59552. $this->setInstallDir($pear_install_dir, $pear_metadata_dir);
  59553. $this->_pearChannel = $pear_channel;
  59554. $this->_peclChannel = $pecl_channel;
  59555. $this->_config = false;
  59556. }
  59557. function setInstallDir($pear_install_dir = PEAR_INSTALL_DIR, $pear_metadata_dir = '')
  59558. {
  59559. $ds = DIRECTORY_SEPARATOR;
  59560. $this->install_dir = $pear_install_dir;
  59561. if (!$pear_metadata_dir) {
  59562. $pear_metadata_dir = $pear_install_dir;
  59563. }
  59564. $this->channelsdir = $pear_metadata_dir.$ds.'.channels';
  59565. $this->statedir = $pear_metadata_dir.$ds.'.registry';
  59566. $this->filemap = $pear_metadata_dir.$ds.'.filemap';
  59567. $this->lockfile = $pear_metadata_dir.$ds.'.lock';
  59568. }
  59569. function hasWriteAccess()
  59570. {
  59571. if (!file_exists($this->install_dir)) {
  59572. $dir = $this->install_dir;
  59573. while ($dir && $dir != '.') {
  59574. $olddir = $dir;
  59575. $dir = dirname($dir);
  59576. if ($dir != '.' && file_exists($dir)) {
  59577. if (is_writeable($dir)) {
  59578. return true;
  59579. }
  59580. return false;
  59581. }
  59582. if ($dir == $olddir) { // this can happen in safe mode
  59583. return @is_writable($dir);
  59584. }
  59585. }
  59586. return false;
  59587. }
  59588. return is_writeable($this->install_dir);
  59589. }
  59590. function setConfig(&$config, $resetInstallDir = true)
  59591. {
  59592. $this->_config = &$config;
  59593. if ($resetInstallDir) {
  59594. $this->setInstallDir($config->get('php_dir'), $config->get('metadata_dir'));
  59595. }
  59596. }
  59597. function _initializeChannelDirs()
  59598. {
  59599. static $running = false;
  59600. if (!$running) {
  59601. $running = true;
  59602. $ds = DIRECTORY_SEPARATOR;
  59603. if (!is_dir($this->channelsdir) ||
  59604. !file_exists($this->channelsdir . $ds . 'pear.php.net.reg') ||
  59605. !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') ||
  59606. !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') ||
  59607. !file_exists($this->channelsdir . $ds . '__uri.reg')) {
  59608. if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
  59609. $pear_channel = $this->_pearChannel;
  59610. if (!is_a($pear_channel, 'PEAR_ChannelFile') || !$pear_channel->validate()) {
  59611. if (!class_exists('PEAR_ChannelFile')) {
  59612. require_once 'PEAR/ChannelFile.php';
  59613. }
  59614. $pear_channel = new PEAR_ChannelFile;
  59615. $pear_channel->setAlias('pear');
  59616. $pear_channel->setServer('pear.php.net');
  59617. $pear_channel->setSummary('PHP Extension and Application Repository');
  59618. $pear_channel->setDefaultPEARProtocols();
  59619. $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/');
  59620. $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/');
  59621. $pear_channel->setBaseURL('REST1.3', 'http://pear.php.net/rest/');
  59622. //$pear_channel->setBaseURL('REST1.4', 'http://pear.php.net/rest/');
  59623. } else {
  59624. $pear_channel->setServer('pear.php.net');
  59625. $pear_channel->setAlias('pear');
  59626. }
  59627. $pear_channel->validate();
  59628. $this->_addChannel($pear_channel);
  59629. }
  59630. if (!file_exists($this->channelsdir . $ds . 'pecl.php.net.reg')) {
  59631. $pecl_channel = $this->_peclChannel;
  59632. if (!is_a($pecl_channel, 'PEAR_ChannelFile') || !$pecl_channel->validate()) {
  59633. if (!class_exists('PEAR_ChannelFile')) {
  59634. require_once 'PEAR/ChannelFile.php';
  59635. }
  59636. $pecl_channel = new PEAR_ChannelFile;
  59637. $pecl_channel->setAlias('pecl');
  59638. $pecl_channel->setServer('pecl.php.net');
  59639. $pecl_channel->setSummary('PHP Extension Community Library');
  59640. $pecl_channel->setDefaultPEARProtocols();
  59641. $pecl_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/');
  59642. $pecl_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/');
  59643. $pecl_channel->setValidationPackage('PEAR_Validator_PECL', '1.0');
  59644. } else {
  59645. $pecl_channel->setServer('pecl.php.net');
  59646. $pecl_channel->setAlias('pecl');
  59647. }
  59648. $pecl_channel->validate();
  59649. $this->_addChannel($pecl_channel);
  59650. }
  59651. if (!file_exists($this->channelsdir . $ds . 'doc.php.net.reg')) {
  59652. $doc_channel = $this->_docChannel;
  59653. if (!is_a($doc_channel, 'PEAR_ChannelFile') || !$doc_channel->validate()) {
  59654. if (!class_exists('PEAR_ChannelFile')) {
  59655. require_once 'PEAR/ChannelFile.php';
  59656. }
  59657. $doc_channel = new PEAR_ChannelFile;
  59658. $doc_channel->setAlias('phpdocs');
  59659. $doc_channel->setServer('doc.php.net');
  59660. $doc_channel->setSummary('PHP Documentation Team');
  59661. $doc_channel->setDefaultPEARProtocols();
  59662. $doc_channel->setBaseURL('REST1.0', 'http://doc.php.net/rest/');
  59663. $doc_channel->setBaseURL('REST1.1', 'http://doc.php.net/rest/');
  59664. $doc_channel->setBaseURL('REST1.3', 'http://doc.php.net/rest/');
  59665. } else {
  59666. $doc_channel->setServer('doc.php.net');
  59667. $doc_channel->setAlias('doc');
  59668. }
  59669. $doc_channel->validate();
  59670. $this->_addChannel($doc_channel);
  59671. }
  59672. if (!file_exists($this->channelsdir . $ds . '__uri.reg')) {
  59673. if (!class_exists('PEAR_ChannelFile')) {
  59674. require_once 'PEAR/ChannelFile.php';
  59675. }
  59676. $private = new PEAR_ChannelFile;
  59677. $private->setName('__uri');
  59678. $private->setDefaultPEARProtocols();
  59679. $private->setBaseURL('REST1.0', '****');
  59680. $private->setSummary('Pseudo-channel for static packages');
  59681. $this->_addChannel($private);
  59682. }
  59683. $this->_rebuildFileMap();
  59684. }
  59685. $running = false;
  59686. }
  59687. }
  59688. function _initializeDirs()
  59689. {
  59690. $ds = DIRECTORY_SEPARATOR;
  59691. // XXX Compatibility code should be removed in the future
  59692. // rename all registry files if any to lowercase
  59693. if (!OS_WINDOWS && file_exists($this->statedir) && is_dir($this->statedir) &&
  59694. $handle = opendir($this->statedir)) {
  59695. $dest = $this->statedir . $ds;
  59696. while (false !== ($file = readdir($handle))) {
  59697. if (preg_match('/^.*[A-Z].*\.reg\\z/', $file)) {
  59698. rename($dest . $file, $dest . strtolower($file));
  59699. }
  59700. }
  59701. closedir($handle);
  59702. }
  59703. $this->_initializeChannelDirs();
  59704. if (!file_exists($this->filemap)) {
  59705. $this->_rebuildFileMap();
  59706. }
  59707. $this->_initializeDepDB();
  59708. }
  59709. function _initializeDepDB()
  59710. {
  59711. if (!isset($this->_dependencyDB)) {
  59712. static $initializing = false;
  59713. if (!$initializing) {
  59714. $initializing = true;
  59715. if (!$this->_config) { // never used?
  59716. $file = OS_WINDOWS ? 'pear.ini' : '.pearrc';
  59717. $this->_config = new PEAR_Config($this->statedir . DIRECTORY_SEPARATOR .
  59718. $file);
  59719. $this->_config->setRegistry($this);
  59720. $this->_config->set('php_dir', $this->install_dir);
  59721. }
  59722. $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config);
  59723. if (PEAR::isError($this->_dependencyDB)) {
  59724. // attempt to recover by removing the dep db
  59725. if (file_exists($this->_config->get('metadata_dir', null, 'pear.php.net') .
  59726. DIRECTORY_SEPARATOR . '.depdb')) {
  59727. @unlink($this->_config->get('metadata_dir', null, 'pear.php.net') .
  59728. DIRECTORY_SEPARATOR . '.depdb');
  59729. }
  59730. $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config);
  59731. if (PEAR::isError($this->_dependencyDB)) {
  59732. echo $this->_dependencyDB->getMessage();
  59733. echo 'Unrecoverable error';
  59734. exit(1);
  59735. }
  59736. }
  59737. $initializing = false;
  59738. }
  59739. }
  59740. }
  59741. /**
  59742. * PEAR_Registry destructor. Makes sure no locks are forgotten.
  59743. *
  59744. * @access private
  59745. */
  59746. function _PEAR_Registry()
  59747. {
  59748. parent::_PEAR();
  59749. if (is_resource($this->lock_fp)) {
  59750. $this->_unlock();
  59751. }
  59752. }
  59753. /**
  59754. * Make sure the directory where we keep registry files exists.
  59755. *
  59756. * @return bool TRUE if directory exists, FALSE if it could not be
  59757. * created
  59758. *
  59759. * @access private
  59760. */
  59761. function _assertStateDir($channel = false)
  59762. {
  59763. if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
  59764. return $this->_assertChannelStateDir($channel);
  59765. }
  59766. static $init = false;
  59767. if (!file_exists($this->statedir)) {
  59768. if (!$this->hasWriteAccess()) {
  59769. return false;
  59770. }
  59771. require_once 'System.php';
  59772. if (!System::mkdir(array('-p', $this->statedir))) {
  59773. return $this->raiseError("could not create directory '{$this->statedir}'");
  59774. }
  59775. $init = true;
  59776. } elseif (!is_dir($this->statedir)) {
  59777. return $this->raiseError('Cannot create directory ' . $this->statedir . ', ' .
  59778. 'it already exists and is not a directory');
  59779. }
  59780. $ds = DIRECTORY_SEPARATOR;
  59781. if (!file_exists($this->channelsdir)) {
  59782. if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg') ||
  59783. !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') ||
  59784. !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') ||
  59785. !file_exists($this->channelsdir . $ds . '__uri.reg')) {
  59786. $init = true;
  59787. }
  59788. } elseif (!is_dir($this->channelsdir)) {
  59789. return $this->raiseError('Cannot create directory ' . $this->channelsdir . ', ' .
  59790. 'it already exists and is not a directory');
  59791. }
  59792. if ($init) {
  59793. static $running = false;
  59794. if (!$running) {
  59795. $running = true;
  59796. $this->_initializeDirs();
  59797. $running = false;
  59798. $init = false;
  59799. }
  59800. } else {
  59801. $this->_initializeDepDB();
  59802. }
  59803. return true;
  59804. }
  59805. /**
  59806. * Make sure the directory where we keep registry files exists for a non-standard channel.
  59807. *
  59808. * @param string channel name
  59809. * @return bool TRUE if directory exists, FALSE if it could not be
  59810. * created
  59811. *
  59812. * @access private
  59813. */
  59814. function _assertChannelStateDir($channel)
  59815. {
  59816. $ds = DIRECTORY_SEPARATOR;
  59817. if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
  59818. if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
  59819. $this->_initializeChannelDirs();
  59820. }
  59821. return $this->_assertStateDir($channel);
  59822. }
  59823. $channelDir = $this->_channelDirectoryName($channel);
  59824. if (!is_dir($this->channelsdir) ||
  59825. !file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
  59826. $this->_initializeChannelDirs();
  59827. }
  59828. if (!file_exists($channelDir)) {
  59829. if (!$this->hasWriteAccess()) {
  59830. return false;
  59831. }
  59832. require_once 'System.php';
  59833. if (!System::mkdir(array('-p', $channelDir))) {
  59834. return $this->raiseError("could not create directory '" . $channelDir .
  59835. "'");
  59836. }
  59837. } elseif (!is_dir($channelDir)) {
  59838. return $this->raiseError("could not create directory '" . $channelDir .
  59839. "', already exists and is not a directory");
  59840. }
  59841. return true;
  59842. }
  59843. /**
  59844. * Make sure the directory where we keep registry files for channels exists
  59845. *
  59846. * @return bool TRUE if directory exists, FALSE if it could not be
  59847. * created
  59848. *
  59849. * @access private
  59850. */
  59851. function _assertChannelDir()
  59852. {
  59853. if (!file_exists($this->channelsdir)) {
  59854. if (!$this->hasWriteAccess()) {
  59855. return false;
  59856. }
  59857. require_once 'System.php';
  59858. if (!System::mkdir(array('-p', $this->channelsdir))) {
  59859. return $this->raiseError("could not create directory '{$this->channelsdir}'");
  59860. }
  59861. } elseif (!is_dir($this->channelsdir)) {
  59862. return $this->raiseError("could not create directory '{$this->channelsdir}" .
  59863. "', it already exists and is not a directory");
  59864. }
  59865. if (!file_exists($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) {
  59866. if (!$this->hasWriteAccess()) {
  59867. return false;
  59868. }
  59869. require_once 'System.php';
  59870. if (!System::mkdir(array('-p', $this->channelsdir . DIRECTORY_SEPARATOR . '.alias'))) {
  59871. return $this->raiseError("could not create directory '{$this->channelsdir}/.alias'");
  59872. }
  59873. } elseif (!is_dir($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) {
  59874. return $this->raiseError("could not create directory '{$this->channelsdir}" .
  59875. "/.alias', it already exists and is not a directory");
  59876. }
  59877. return true;
  59878. }
  59879. /**
  59880. * Get the name of the file where data for a given package is stored.
  59881. *
  59882. * @param string channel name, or false if this is a PEAR package
  59883. * @param string package name
  59884. *
  59885. * @return string registry file name
  59886. *
  59887. * @access public
  59888. */
  59889. function _packageFileName($package, $channel = false)
  59890. {
  59891. if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
  59892. return $this->_channelDirectoryName($channel) . DIRECTORY_SEPARATOR .
  59893. strtolower($package) . '.reg';
  59894. }
  59895. return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg';
  59896. }
  59897. /**
  59898. * Get the name of the file where data for a given channel is stored.
  59899. * @param string channel name
  59900. * @return string registry file name
  59901. */
  59902. function _channelFileName($channel, $noaliases = false)
  59903. {
  59904. if (!$noaliases) {
  59905. if (file_exists($this->_getChannelAliasFileName($channel))) {
  59906. $channel = implode('', file($this->_getChannelAliasFileName($channel)));
  59907. }
  59908. }
  59909. return $this->channelsdir . DIRECTORY_SEPARATOR . str_replace('/', '_',
  59910. strtolower($channel)) . '.reg';
  59911. }
  59912. /**
  59913. * @param string
  59914. * @return string
  59915. */
  59916. function _getChannelAliasFileName($alias)
  59917. {
  59918. return $this->channelsdir . DIRECTORY_SEPARATOR . '.alias' .
  59919. DIRECTORY_SEPARATOR . str_replace('/', '_', strtolower($alias)) . '.txt';
  59920. }
  59921. /**
  59922. * Get the name of a channel from its alias
  59923. */
  59924. function _getChannelFromAlias($channel)
  59925. {
  59926. if (!$this->_channelExists($channel)) {
  59927. if ($channel == 'pear.php.net') {
  59928. return 'pear.php.net';
  59929. }
  59930. if ($channel == 'pecl.php.net') {
  59931. return 'pecl.php.net';
  59932. }
  59933. if ($channel == 'doc.php.net') {
  59934. return 'doc.php.net';
  59935. }
  59936. if ($channel == '__uri') {
  59937. return '__uri';
  59938. }
  59939. return false;
  59940. }
  59941. $channel = strtolower($channel);
  59942. if (file_exists($this->_getChannelAliasFileName($channel))) {
  59943. // translate an alias to an actual channel
  59944. return implode('', file($this->_getChannelAliasFileName($channel)));
  59945. }
  59946. return $channel;
  59947. }
  59948. /**
  59949. * Get the alias of a channel from its alias or its name
  59950. */
  59951. function _getAlias($channel)
  59952. {
  59953. if (!$this->_channelExists($channel)) {
  59954. if ($channel == 'pear.php.net') {
  59955. return 'pear';
  59956. }
  59957. if ($channel == 'pecl.php.net') {
  59958. return 'pecl';
  59959. }
  59960. if ($channel == 'doc.php.net') {
  59961. return 'phpdocs';
  59962. }
  59963. return false;
  59964. }
  59965. $channel = $this->_getChannel($channel);
  59966. if (PEAR::isError($channel)) {
  59967. return $channel;
  59968. }
  59969. return $channel->getAlias();
  59970. }
  59971. /**
  59972. * Get the name of the file where data for a given package is stored.
  59973. *
  59974. * @param string channel name, or false if this is a PEAR package
  59975. * @param string package name
  59976. *
  59977. * @return string registry file name
  59978. *
  59979. * @access public
  59980. */
  59981. function _channelDirectoryName($channel)
  59982. {
  59983. if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
  59984. return $this->statedir;
  59985. }
  59986. $ch = $this->_getChannelFromAlias($channel);
  59987. if (!$ch) {
  59988. $ch = $channel;
  59989. }
  59990. return $this->statedir . DIRECTORY_SEPARATOR . strtolower('.channel.' .
  59991. str_replace('/', '_', $ch));
  59992. }
  59993. function _openPackageFile($package, $mode, $channel = false)
  59994. {
  59995. if (!$this->_assertStateDir($channel)) {
  59996. return null;
  59997. }
  59998. if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) {
  59999. return null;
  60000. }
  60001. $file = $this->_packageFileName($package, $channel);
  60002. if (!file_exists($file) && $mode == 'r' || $mode == 'rb') {
  60003. return null;
  60004. }
  60005. $fp = @fopen($file, $mode);
  60006. if (!$fp) {
  60007. return null;
  60008. }
  60009. return $fp;
  60010. }
  60011. function _closePackageFile($fp)
  60012. {
  60013. fclose($fp);
  60014. }
  60015. function _openChannelFile($channel, $mode)
  60016. {
  60017. if (!$this->_assertChannelDir()) {
  60018. return null;
  60019. }
  60020. if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) {
  60021. return null;
  60022. }
  60023. $file = $this->_channelFileName($channel);
  60024. if (!file_exists($file) && $mode == 'r' || $mode == 'rb') {
  60025. return null;
  60026. }
  60027. $fp = @fopen($file, $mode);
  60028. if (!$fp) {
  60029. return null;
  60030. }
  60031. return $fp;
  60032. }
  60033. function _closeChannelFile($fp)
  60034. {
  60035. fclose($fp);
  60036. }
  60037. function _rebuildFileMap()
  60038. {
  60039. if (!class_exists('PEAR_Installer_Role')) {
  60040. require_once 'PEAR/Installer/Role.php';
  60041. }
  60042. $channels = $this->_listAllPackages();
  60043. $files = array();
  60044. foreach ($channels as $channel => $packages) {
  60045. foreach ($packages as $package) {
  60046. $version = $this->_packageInfo($package, 'version', $channel);
  60047. $filelist = $this->_packageInfo($package, 'filelist', $channel);
  60048. if (!is_array($filelist)) {
  60049. continue;
  60050. }
  60051. foreach ($filelist as $name => $attrs) {
  60052. if (isset($attrs['attribs'])) {
  60053. $attrs = $attrs['attribs'];
  60054. }
  60055. // it is possible for conflicting packages in different channels to
  60056. // conflict with data files/doc files
  60057. if ($name == 'dirtree') {
  60058. continue;
  60059. }
  60060. if (isset($attrs['role']) && !in_array($attrs['role'],
  60061. PEAR_Installer_Role::getInstallableRoles())) {
  60062. // these are not installed
  60063. continue;
  60064. }
  60065. if (isset($attrs['role']) && !in_array($attrs['role'],
  60066. PEAR_Installer_Role::getBaseinstallRoles())) {
  60067. $attrs['baseinstalldir'] = $package;
  60068. }
  60069. if (isset($attrs['baseinstalldir'])) {
  60070. $file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name;
  60071. } else {
  60072. $file = $name;
  60073. }
  60074. $file = preg_replace(',^/+,', '', $file);
  60075. if ($channel != 'pear.php.net') {
  60076. if (!isset($files[$attrs['role']])) {
  60077. $files[$attrs['role']] = array();
  60078. }
  60079. $files[$attrs['role']][$file] = array(strtolower($channel),
  60080. strtolower($package));
  60081. } else {
  60082. if (!isset($files[$attrs['role']])) {
  60083. $files[$attrs['role']] = array();
  60084. }
  60085. $files[$attrs['role']][$file] = strtolower($package);
  60086. }
  60087. }
  60088. }
  60089. }
  60090. $this->_assertStateDir();
  60091. if (!$this->hasWriteAccess()) {
  60092. return false;
  60093. }
  60094. $fp = @fopen($this->filemap, 'wb');
  60095. if (!$fp) {
  60096. return false;
  60097. }
  60098. $this->filemap_cache = $files;
  60099. fwrite($fp, serialize($files));
  60100. fclose($fp);
  60101. return true;
  60102. }
  60103. function _readFileMap()
  60104. {
  60105. if (!file_exists($this->filemap)) {
  60106. return array();
  60107. }
  60108. $fp = @fopen($this->filemap, 'r');
  60109. if (!$fp) {
  60110. $last_errormsg = '';
  60111. $last_error = error_get_last();
  60112. if (!empty($last_error['message'])) {
  60113. $last_errormsg = $last_error['message'];
  60114. }
  60115. return $this->raiseError('PEAR_Registry: could not open filemap "' . $this->filemap . '"', PEAR_REGISTRY_ERROR_FILE, null, null, $last_errormsg);
  60116. }
  60117. clearstatcache();
  60118. $fsize = filesize($this->filemap);
  60119. fclose($fp);
  60120. $data = file_get_contents($this->filemap);
  60121. $tmp = unserialize($data);
  60122. if (!$tmp && $fsize > 7) {
  60123. return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data);
  60124. }
  60125. $this->filemap_cache = $tmp;
  60126. return true;
  60127. }
  60128. /**
  60129. * Lock the registry.
  60130. *
  60131. * @param integer lock mode, one of LOCK_EX, LOCK_SH or LOCK_UN.
  60132. * See flock manual for more information.
  60133. *
  60134. * @return bool TRUE on success, FALSE if locking failed, or a
  60135. * PEAR error if some other error occurs (such as the
  60136. * lock file not being writable).
  60137. *
  60138. * @access private
  60139. */
  60140. function _lock($mode = LOCK_EX)
  60141. {
  60142. if (stristr(php_uname(), 'Windows 9')) {
  60143. return true;
  60144. }
  60145. if ($mode != LOCK_UN && is_resource($this->lock_fp)) {
  60146. // XXX does not check type of lock (LOCK_SH/LOCK_EX)
  60147. return true;
  60148. }
  60149. if (!$this->_assertStateDir()) {
  60150. if ($mode == LOCK_EX) {
  60151. return $this->raiseError('Registry directory is not writeable by the current user');
  60152. }
  60153. return true;
  60154. }
  60155. $open_mode = 'w';
  60156. // XXX People reported problems with LOCK_SH and 'w'
  60157. if ($mode === LOCK_SH || $mode === LOCK_UN) {
  60158. if (!file_exists($this->lockfile)) {
  60159. touch($this->lockfile);
  60160. }
  60161. $open_mode = 'r';
  60162. }
  60163. if (!is_resource($this->lock_fp)) {
  60164. $this->lock_fp = @fopen($this->lockfile, $open_mode);
  60165. }
  60166. if (!is_resource($this->lock_fp)) {
  60167. $this->lock_fp = null;
  60168. return $this->raiseError("could not create lock file" .
  60169. (isset($php_errormsg) ? ": " . $php_errormsg : ""));
  60170. }
  60171. if (!(int)flock($this->lock_fp, $mode)) {
  60172. switch ($mode) {
  60173. case LOCK_SH: $str = 'shared'; break;
  60174. case LOCK_EX: $str = 'exclusive'; break;
  60175. case LOCK_UN: $str = 'unlock'; break;
  60176. default: $str = 'unknown'; break;
  60177. }
  60178. //is resource at this point, close it on error.
  60179. fclose($this->lock_fp);
  60180. $this->lock_fp = null;
  60181. return $this->raiseError("could not acquire $str lock ($this->lockfile)",
  60182. PEAR_REGISTRY_ERROR_LOCK);
  60183. }
  60184. return true;
  60185. }
  60186. function _unlock()
  60187. {
  60188. $ret = $this->_lock(LOCK_UN);
  60189. if (is_resource($this->lock_fp)) {
  60190. fclose($this->lock_fp);
  60191. }
  60192. $this->lock_fp = null;
  60193. return $ret;
  60194. }
  60195. function _packageExists($package, $channel = false)
  60196. {
  60197. return file_exists($this->_packageFileName($package, $channel));
  60198. }
  60199. /**
  60200. * Determine whether a channel exists in the registry
  60201. *
  60202. * @param string Channel name
  60203. * @param bool if true, then aliases will be ignored
  60204. * @return boolean
  60205. */
  60206. function _channelExists($channel, $noaliases = false)
  60207. {
  60208. $a = file_exists($this->_channelFileName($channel, $noaliases));
  60209. if (!$a && $channel == 'pear.php.net') {
  60210. return true;
  60211. }
  60212. if (!$a && $channel == 'pecl.php.net') {
  60213. return true;
  60214. }
  60215. if (!$a && $channel == 'doc.php.net') {
  60216. return true;
  60217. }
  60218. return $a;
  60219. }
  60220. /**
  60221. * Determine whether a mirror exists within the default channel in the registry
  60222. *
  60223. * @param string Channel name
  60224. * @param string Mirror name
  60225. *
  60226. * @return boolean
  60227. */
  60228. function _mirrorExists($channel, $mirror)
  60229. {
  60230. $data = $this->_channelInfo($channel);
  60231. if (!isset($data['servers']['mirror'])) {
  60232. return false;
  60233. }
  60234. foreach ($data['servers']['mirror'] as $m) {
  60235. if ($m['attribs']['host'] == $mirror) {
  60236. return true;
  60237. }
  60238. }
  60239. return false;
  60240. }
  60241. /**
  60242. * @param PEAR_ChannelFile Channel object
  60243. * @param donotuse
  60244. * @param string Last-Modified HTTP tag from remote request
  60245. * @return boolean|PEAR_Error True on creation, false if it already exists
  60246. */
  60247. function _addChannel($channel, $update = false, $lastmodified = false)
  60248. {
  60249. if (!is_a($channel, 'PEAR_ChannelFile')) {
  60250. return false;
  60251. }
  60252. if (!$channel->validate()) {
  60253. return false;
  60254. }
  60255. if (file_exists($this->_channelFileName($channel->getName()))) {
  60256. if (!$update) {
  60257. return false;
  60258. }
  60259. $checker = $this->_getChannel($channel->getName());
  60260. if (PEAR::isError($checker)) {
  60261. return $checker;
  60262. }
  60263. if ($channel->getAlias() != $checker->getAlias()) {
  60264. if (file_exists($this->_getChannelAliasFileName($checker->getAlias()))) {
  60265. @unlink($this->_getChannelAliasFileName($checker->getAlias()));
  60266. }
  60267. }
  60268. } else {
  60269. if ($update && !in_array($channel->getName(), array('pear.php.net', 'pecl.php.net', 'doc.php.net'))) {
  60270. return false;
  60271. }
  60272. }
  60273. $ret = $this->_assertChannelDir();
  60274. if (PEAR::isError($ret)) {
  60275. return $ret;
  60276. }
  60277. $ret = $this->_assertChannelStateDir($channel->getName());
  60278. if (PEAR::isError($ret)) {
  60279. return $ret;
  60280. }
  60281. if ($channel->getAlias() != $channel->getName()) {
  60282. if (file_exists($this->_getChannelAliasFileName($channel->getAlias())) &&
  60283. $this->_getChannelFromAlias($channel->getAlias()) != $channel->getName()) {
  60284. $channel->setAlias($channel->getName());
  60285. }
  60286. if (!$this->hasWriteAccess()) {
  60287. return false;
  60288. }
  60289. $fp = @fopen($this->_getChannelAliasFileName($channel->getAlias()), 'w');
  60290. if (!$fp) {
  60291. return false;
  60292. }
  60293. fwrite($fp, $channel->getName());
  60294. fclose($fp);
  60295. }
  60296. if (!$this->hasWriteAccess()) {
  60297. return false;
  60298. }
  60299. $fp = @fopen($this->_channelFileName($channel->getName()), 'wb');
  60300. if (!$fp) {
  60301. return false;
  60302. }
  60303. $info = $channel->toArray();
  60304. if ($lastmodified) {
  60305. $info['_lastmodified'] = $lastmodified;
  60306. } else {
  60307. $info['_lastmodified'] = self::getSourceDateEpoch();
  60308. }
  60309. fwrite($fp, serialize($info));
  60310. fclose($fp);
  60311. return true;
  60312. }
  60313. /**
  60314. * Deletion fails if there are any packages installed from the channel
  60315. * @param string|PEAR_ChannelFile channel name
  60316. * @return boolean|PEAR_Error True on deletion, false if it doesn't exist
  60317. */
  60318. function _deleteChannel($channel)
  60319. {
  60320. if (!is_string($channel)) {
  60321. if (!is_a($channel, 'PEAR_ChannelFile')) {
  60322. return false;
  60323. }
  60324. if (!$channel->validate()) {
  60325. return false;
  60326. }
  60327. $channel = $channel->getName();
  60328. }
  60329. if ($this->_getChannelFromAlias($channel) == '__uri') {
  60330. return false;
  60331. }
  60332. if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') {
  60333. return false;
  60334. }
  60335. if ($this->_getChannelFromAlias($channel) == 'doc.php.net') {
  60336. return false;
  60337. }
  60338. if (!$this->_channelExists($channel)) {
  60339. return false;
  60340. }
  60341. if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
  60342. return false;
  60343. }
  60344. $channel = $this->_getChannelFromAlias($channel);
  60345. if ($channel == 'pear.php.net') {
  60346. return false;
  60347. }
  60348. $test = $this->_listChannelPackages($channel);
  60349. if (count($test)) {
  60350. return false;
  60351. }
  60352. $test = @rmdir($this->_channelDirectoryName($channel));
  60353. if (!$test) {
  60354. return false;
  60355. }
  60356. $file = $this->_getChannelAliasFileName($this->_getAlias($channel));
  60357. if (file_exists($file)) {
  60358. $test = @unlink($file);
  60359. if (!$test) {
  60360. return false;
  60361. }
  60362. }
  60363. $file = $this->_channelFileName($channel);
  60364. $ret = true;
  60365. if (file_exists($file)) {
  60366. $ret = @unlink($file);
  60367. }
  60368. return $ret;
  60369. }
  60370. /**
  60371. * Determine whether a channel exists in the registry
  60372. * @param string Channel Alias
  60373. * @return boolean
  60374. */
  60375. function _isChannelAlias($alias)
  60376. {
  60377. return file_exists($this->_getChannelAliasFileName($alias));
  60378. }
  60379. /**
  60380. * @param string|null
  60381. * @param string|null
  60382. * @param string|null
  60383. * @return array|null
  60384. * @access private
  60385. */
  60386. function _packageInfo($package = null, $key = null, $channel = 'pear.php.net')
  60387. {
  60388. if ($package === null) {
  60389. if ($channel === null) {
  60390. $channels = $this->_listChannels();
  60391. $ret = array();
  60392. foreach ($channels as $channel) {
  60393. $channel = strtolower($channel);
  60394. $ret[$channel] = array();
  60395. $packages = $this->_listPackages($channel);
  60396. foreach ($packages as $package) {
  60397. $ret[$channel][] = $this->_packageInfo($package, null, $channel);
  60398. }
  60399. }
  60400. return $ret;
  60401. }
  60402. $ps = $this->_listPackages($channel);
  60403. if (!count($ps)) {
  60404. return array();
  60405. }
  60406. return array_map(array(&$this, '_packageInfo'),
  60407. $ps, array_fill(0, count($ps), null),
  60408. array_fill(0, count($ps), $channel));
  60409. }
  60410. $fp = $this->_openPackageFile($package, 'r', $channel);
  60411. if ($fp === null) {
  60412. return null;
  60413. }
  60414. clearstatcache();
  60415. $this->_closePackageFile($fp);
  60416. $data = file_get_contents($this->_packageFileName($package, $channel));
  60417. $data = @unserialize($data);
  60418. if ($key === null) {
  60419. return $data;
  60420. }
  60421. // compatibility for package.xml version 2.0
  60422. if (isset($data['old'][$key])) {
  60423. return $data['old'][$key];
  60424. }
  60425. if (isset($data[$key])) {
  60426. return $data[$key];
  60427. }
  60428. return null;
  60429. }
  60430. /**
  60431. * @param string Channel name
  60432. * @param bool whether to strictly retrieve info of channels, not just aliases
  60433. * @return array|null
  60434. */
  60435. function _channelInfo($channel, $noaliases = false)
  60436. {
  60437. if (!$this->_channelExists($channel, $noaliases)) {
  60438. return null;
  60439. }
  60440. $fp = $this->_openChannelFile($channel, 'r');
  60441. if ($fp === null) {
  60442. return null;
  60443. }
  60444. clearstatcache();
  60445. $this->_closeChannelFile($fp);
  60446. $data = file_get_contents($this->_channelFileName($channel));
  60447. $data = unserialize($data);
  60448. return $data;
  60449. }
  60450. function _listChannels()
  60451. {
  60452. $channellist = array();
  60453. if (!file_exists($this->channelsdir) || !is_dir($this->channelsdir)) {
  60454. return array('pear.php.net', 'pecl.php.net', 'doc.php.net', '__uri');
  60455. }
  60456. $dp = opendir($this->channelsdir);
  60457. while ($ent = readdir($dp)) {
  60458. if ($ent[0] == '.' || substr($ent, -4) != '.reg') {
  60459. continue;
  60460. }
  60461. if ($ent == '__uri.reg') {
  60462. $channellist[] = '__uri';
  60463. continue;
  60464. }
  60465. $channellist[] = str_replace('_', '/', substr($ent, 0, -4));
  60466. }
  60467. closedir($dp);
  60468. if (!in_array('pear.php.net', $channellist)) {
  60469. $channellist[] = 'pear.php.net';
  60470. }
  60471. if (!in_array('pecl.php.net', $channellist)) {
  60472. $channellist[] = 'pecl.php.net';
  60473. }
  60474. if (!in_array('doc.php.net', $channellist)) {
  60475. $channellist[] = 'doc.php.net';
  60476. }
  60477. if (!in_array('__uri', $channellist)) {
  60478. $channellist[] = '__uri';
  60479. }
  60480. natsort($channellist);
  60481. return $channellist;
  60482. }
  60483. function _listPackages($channel = false)
  60484. {
  60485. if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
  60486. return $this->_listChannelPackages($channel);
  60487. }
  60488. if (!file_exists($this->statedir) || !is_dir($this->statedir)) {
  60489. return array();
  60490. }
  60491. $pkglist = array();
  60492. $dp = opendir($this->statedir);
  60493. if (!$dp) {
  60494. return $pkglist;
  60495. }
  60496. while ($ent = readdir($dp)) {
  60497. if ($ent[0] == '.' || substr($ent, -4) != '.reg') {
  60498. continue;
  60499. }
  60500. $pkglist[] = substr($ent, 0, -4);
  60501. }
  60502. closedir($dp);
  60503. sort($pkglist);
  60504. return $pkglist;
  60505. }
  60506. function _listChannelPackages($channel)
  60507. {
  60508. $pkglist = array();
  60509. if (!file_exists($this->_channelDirectoryName($channel)) ||
  60510. !is_dir($this->_channelDirectoryName($channel))) {
  60511. return array();
  60512. }
  60513. $dp = opendir($this->_channelDirectoryName($channel));
  60514. if (!$dp) {
  60515. return $pkglist;
  60516. }
  60517. while ($ent = readdir($dp)) {
  60518. if ($ent[0] == '.' || substr($ent, -4) != '.reg') {
  60519. continue;
  60520. }
  60521. $pkglist[] = substr($ent, 0, -4);
  60522. }
  60523. closedir($dp);
  60524. return $pkglist;
  60525. }
  60526. function _listAllPackages()
  60527. {
  60528. $ret = array();
  60529. foreach ($this->_listChannels() as $channel) {
  60530. $ret[$channel] = $this->_listPackages($channel);
  60531. }
  60532. return $ret;
  60533. }
  60534. /**
  60535. * Add an installed package to the registry
  60536. * @param string package name
  60537. * @param array package info (parsed by PEAR_Common::infoFrom*() methods)
  60538. * @return bool success of saving
  60539. * @access private
  60540. */
  60541. function _addPackage($package, $info)
  60542. {
  60543. if ($this->_packageExists($package)) {
  60544. return false;
  60545. }
  60546. $fp = $this->_openPackageFile($package, 'wb');
  60547. if ($fp === null) {
  60548. return false;
  60549. }
  60550. $info['_lastmodified'] = self::getSourceDateEpoch();
  60551. fwrite($fp, serialize($info));
  60552. $this->_closePackageFile($fp);
  60553. if (isset($info['filelist'])) {
  60554. $this->_rebuildFileMap();
  60555. }
  60556. return true;
  60557. }
  60558. /**
  60559. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  60560. * @return bool
  60561. * @access private
  60562. */
  60563. function _addPackage2($info)
  60564. {
  60565. if (!is_a($info, 'PEAR_PackageFile_v1') && !is_a($info, 'PEAR_PackageFile_v2')) {
  60566. return false;
  60567. }
  60568. if (!$info->validate()) {
  60569. if (class_exists('PEAR_Common')) {
  60570. $ui = PEAR_Frontend::singleton();
  60571. if ($ui) {
  60572. foreach ($info->getValidationWarnings() as $err) {
  60573. $ui->log($err['message'], true);
  60574. }
  60575. }
  60576. }
  60577. return false;
  60578. }
  60579. $channel = $info->getChannel();
  60580. $package = $info->getPackage();
  60581. $save = $info;
  60582. if ($this->_packageExists($package, $channel)) {
  60583. return false;
  60584. }
  60585. if (!$this->_channelExists($channel, true)) {
  60586. return false;
  60587. }
  60588. $info = $info->toArray(true);
  60589. if (!$info) {
  60590. return false;
  60591. }
  60592. $fp = $this->_openPackageFile($package, 'wb', $channel);
  60593. if ($fp === null) {
  60594. return false;
  60595. }
  60596. $info['_lastmodified'] = self::getSourceDateEpoch();
  60597. fwrite($fp, serialize($info));
  60598. $this->_closePackageFile($fp);
  60599. $this->_rebuildFileMap();
  60600. return true;
  60601. }
  60602. /**
  60603. * @param string Package name
  60604. * @param array parsed package.xml 1.0
  60605. * @param bool this parameter is only here for BC. Don't use it.
  60606. * @access private
  60607. */
  60608. function _updatePackage($package, $info, $merge = true)
  60609. {
  60610. $oldinfo = $this->_packageInfo($package);
  60611. if (empty($oldinfo)) {
  60612. return false;
  60613. }
  60614. $fp = $this->_openPackageFile($package, 'w');
  60615. if ($fp === null) {
  60616. return false;
  60617. }
  60618. if (is_object($info)) {
  60619. $info = $info->toArray();
  60620. }
  60621. $info['_lastmodified'] = self::getSourceDateEpoch();
  60622. $newinfo = $info;
  60623. if ($merge) {
  60624. $info = array_merge($oldinfo, $info);
  60625. } else {
  60626. $diff = $info;
  60627. }
  60628. fwrite($fp, serialize($info));
  60629. $this->_closePackageFile($fp);
  60630. if (isset($newinfo['filelist'])) {
  60631. $this->_rebuildFileMap();
  60632. }
  60633. return true;
  60634. }
  60635. /**
  60636. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  60637. * @return bool
  60638. * @access private
  60639. */
  60640. function _updatePackage2($info)
  60641. {
  60642. if (!$this->_packageExists($info->getPackage(), $info->getChannel())) {
  60643. return false;
  60644. }
  60645. $fp = $this->_openPackageFile($info->getPackage(), 'w', $info->getChannel());
  60646. if ($fp === null) {
  60647. return false;
  60648. }
  60649. $save = $info;
  60650. $info = $save->getArray(true);
  60651. $info['_lastmodified'] = self::getSourceDateEpoch();
  60652. fwrite($fp, serialize($info));
  60653. $this->_closePackageFile($fp);
  60654. $this->_rebuildFileMap();
  60655. return true;
  60656. }
  60657. /**
  60658. * @param string Package name
  60659. * @param string Channel name
  60660. * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null
  60661. * @access private
  60662. */
  60663. function &_getPackage($package, $channel = 'pear.php.net')
  60664. {
  60665. $info = $this->_packageInfo($package, null, $channel);
  60666. if ($info === null) {
  60667. return $info;
  60668. }
  60669. $a = $this->_config;
  60670. if (!$a) {
  60671. $this->_config = new PEAR_Config;
  60672. $this->_config->set('php_dir', $this->statedir);
  60673. }
  60674. if (!class_exists('PEAR_PackageFile')) {
  60675. require_once 'PEAR/PackageFile.php';
  60676. }
  60677. $pkg = new PEAR_PackageFile($this->_config);
  60678. $pf = &$pkg->fromArray($info);
  60679. return $pf;
  60680. }
  60681. /**
  60682. * @param string channel name
  60683. * @param bool whether to strictly retrieve channel names
  60684. * @return PEAR_ChannelFile|PEAR_Error
  60685. * @access private
  60686. */
  60687. function &_getChannel($channel, $noaliases = false)
  60688. {
  60689. $ch = false;
  60690. if ($this->_channelExists($channel, $noaliases)) {
  60691. $chinfo = $this->_channelInfo($channel, $noaliases);
  60692. if ($chinfo) {
  60693. if (!class_exists('PEAR_ChannelFile')) {
  60694. require_once 'PEAR/ChannelFile.php';
  60695. }
  60696. $ch = &PEAR_ChannelFile::fromArrayWithErrors($chinfo);
  60697. }
  60698. }
  60699. if ($ch) {
  60700. if ($ch->validate()) {
  60701. return $ch;
  60702. }
  60703. foreach ($ch->getErrors(true) as $err) {
  60704. $message = $err['message'] . "\n";
  60705. }
  60706. $ch = PEAR::raiseError($message);
  60707. return $ch;
  60708. }
  60709. if ($this->_getChannelFromAlias($channel) == 'pear.php.net') {
  60710. // the registry is not properly set up, so use defaults
  60711. if (!class_exists('PEAR_ChannelFile')) {
  60712. require_once 'PEAR/ChannelFile.php';
  60713. }
  60714. $pear_channel = new PEAR_ChannelFile;
  60715. $pear_channel->setServer('pear.php.net');
  60716. $pear_channel->setAlias('pear');
  60717. $pear_channel->setSummary('PHP Extension and Application Repository');
  60718. $pear_channel->setDefaultPEARProtocols();
  60719. $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/');
  60720. $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/');
  60721. $pear_channel->setBaseURL('REST1.3', 'http://pear.php.net/rest/');
  60722. return $pear_channel;
  60723. }
  60724. if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') {
  60725. // the registry is not properly set up, so use defaults
  60726. if (!class_exists('PEAR_ChannelFile')) {
  60727. require_once 'PEAR/ChannelFile.php';
  60728. }
  60729. $pear_channel = new PEAR_ChannelFile;
  60730. $pear_channel->setServer('pecl.php.net');
  60731. $pear_channel->setAlias('pecl');
  60732. $pear_channel->setSummary('PHP Extension Community Library');
  60733. $pear_channel->setDefaultPEARProtocols();
  60734. $pear_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/');
  60735. $pear_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/');
  60736. $pear_channel->setValidationPackage('PEAR_Validator_PECL', '1.0');
  60737. return $pear_channel;
  60738. }
  60739. if ($this->_getChannelFromAlias($channel) == 'doc.php.net') {
  60740. // the registry is not properly set up, so use defaults
  60741. if (!class_exists('PEAR_ChannelFile')) {
  60742. require_once 'PEAR/ChannelFile.php';
  60743. }
  60744. $doc_channel = new PEAR_ChannelFile;
  60745. $doc_channel->setServer('doc.php.net');
  60746. $doc_channel->setAlias('phpdocs');
  60747. $doc_channel->setSummary('PHP Documentation Team');
  60748. $doc_channel->setDefaultPEARProtocols();
  60749. $doc_channel->setBaseURL('REST1.0', 'http://doc.php.net/rest/');
  60750. $doc_channel->setBaseURL('REST1.1', 'http://doc.php.net/rest/');
  60751. $doc_channel->setBaseURL('REST1.3', 'http://doc.php.net/rest/');
  60752. return $doc_channel;
  60753. }
  60754. if ($this->_getChannelFromAlias($channel) == '__uri') {
  60755. // the registry is not properly set up, so use defaults
  60756. if (!class_exists('PEAR_ChannelFile')) {
  60757. require_once 'PEAR/ChannelFile.php';
  60758. }
  60759. $private = new PEAR_ChannelFile;
  60760. $private->setName('__uri');
  60761. $private->setDefaultPEARProtocols();
  60762. $private->setBaseURL('REST1.0', '****');
  60763. $private->setSummary('Pseudo-channel for static packages');
  60764. return $private;
  60765. }
  60766. return $ch;
  60767. }
  60768. /**
  60769. * @param string Package name
  60770. * @param string Channel name
  60771. * @return bool
  60772. */
  60773. function packageExists($package, $channel = 'pear.php.net')
  60774. {
  60775. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  60776. return $e;
  60777. }
  60778. $ret = $this->_packageExists($package, $channel);
  60779. $this->_unlock();
  60780. return $ret;
  60781. }
  60782. // }}}
  60783. // {{{ channelExists()
  60784. /**
  60785. * @param string channel name
  60786. * @param bool if true, then aliases will be ignored
  60787. * @return bool
  60788. */
  60789. function channelExists($channel, $noaliases = false)
  60790. {
  60791. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  60792. return $e;
  60793. }
  60794. $ret = $this->_channelExists($channel, $noaliases);
  60795. $this->_unlock();
  60796. return $ret;
  60797. }
  60798. // }}}
  60799. /**
  60800. * @param string channel name mirror is in
  60801. * @param string mirror name
  60802. *
  60803. * @return bool
  60804. */
  60805. function mirrorExists($channel, $mirror)
  60806. {
  60807. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  60808. return $e;
  60809. }
  60810. $ret = $this->_mirrorExists($channel, $mirror);
  60811. $this->_unlock();
  60812. return $ret;
  60813. }
  60814. // {{{ isAlias()
  60815. /**
  60816. * Determines whether the parameter is an alias of a channel
  60817. * @param string
  60818. * @return bool
  60819. */
  60820. function isAlias($alias)
  60821. {
  60822. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  60823. return $e;
  60824. }
  60825. $ret = $this->_isChannelAlias($alias);
  60826. $this->_unlock();
  60827. return $ret;
  60828. }
  60829. // }}}
  60830. // {{{ packageInfo()
  60831. /**
  60832. * @param string|null
  60833. * @param string|null
  60834. * @param string
  60835. * @return array|null
  60836. */
  60837. function packageInfo($package = null, $key = null, $channel = 'pear.php.net')
  60838. {
  60839. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  60840. return $e;
  60841. }
  60842. $ret = $this->_packageInfo($package, $key, $channel);
  60843. $this->_unlock();
  60844. return $ret;
  60845. }
  60846. // }}}
  60847. // {{{ channelInfo()
  60848. /**
  60849. * Retrieve a raw array of channel data.
  60850. *
  60851. * Do not use this, instead use {@link getChannel()} for normal
  60852. * operations. Array structure is undefined in this method
  60853. * @param string channel name
  60854. * @param bool whether to strictly retrieve information only on non-aliases
  60855. * @return array|null|PEAR_Error
  60856. */
  60857. function channelInfo($channel = null, $noaliases = false)
  60858. {
  60859. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  60860. return $e;
  60861. }
  60862. $ret = $this->_channelInfo($channel, $noaliases);
  60863. $this->_unlock();
  60864. return $ret;
  60865. }
  60866. // }}}
  60867. /**
  60868. * @param string
  60869. */
  60870. function channelName($channel)
  60871. {
  60872. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  60873. return $e;
  60874. }
  60875. $ret = $this->_getChannelFromAlias($channel);
  60876. $this->_unlock();
  60877. return $ret;
  60878. }
  60879. /**
  60880. * @param string
  60881. */
  60882. function channelAlias($channel)
  60883. {
  60884. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  60885. return $e;
  60886. }
  60887. $ret = $this->_getAlias($channel);
  60888. $this->_unlock();
  60889. return $ret;
  60890. }
  60891. // {{{ listPackages()
  60892. function listPackages($channel = false)
  60893. {
  60894. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  60895. return $e;
  60896. }
  60897. $ret = $this->_listPackages($channel);
  60898. $this->_unlock();
  60899. return $ret;
  60900. }
  60901. // }}}
  60902. // {{{ listAllPackages()
  60903. function listAllPackages()
  60904. {
  60905. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  60906. return $e;
  60907. }
  60908. $ret = $this->_listAllPackages();
  60909. $this->_unlock();
  60910. return $ret;
  60911. }
  60912. // }}}
  60913. // {{{ listChannel()
  60914. function listChannels()
  60915. {
  60916. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  60917. return $e;
  60918. }
  60919. $ret = $this->_listChannels();
  60920. $this->_unlock();
  60921. return $ret;
  60922. }
  60923. // }}}
  60924. // {{{ addPackage()
  60925. /**
  60926. * Add an installed package to the registry
  60927. * @param string|PEAR_PackageFile_v1|PEAR_PackageFile_v2 package name or object
  60928. * that will be passed to {@link addPackage2()}
  60929. * @param array package info (parsed by PEAR_Common::infoFrom*() methods)
  60930. * @return bool success of saving
  60931. */
  60932. function addPackage($package, $info)
  60933. {
  60934. if (is_object($info)) {
  60935. return $this->addPackage2($info);
  60936. }
  60937. if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  60938. return $e;
  60939. }
  60940. $ret = $this->_addPackage($package, $info);
  60941. $this->_unlock();
  60942. if ($ret) {
  60943. if (!class_exists('PEAR_PackageFile_v1')) {
  60944. require_once 'PEAR/PackageFile/v1.php';
  60945. }
  60946. $pf = new PEAR_PackageFile_v1;
  60947. $pf->setConfig($this->_config);
  60948. $pf->fromArray($info);
  60949. $this->_dependencyDB->uninstallPackage($pf);
  60950. $this->_dependencyDB->installPackage($pf);
  60951. }
  60952. return $ret;
  60953. }
  60954. // }}}
  60955. // {{{ addPackage2()
  60956. function addPackage2($info)
  60957. {
  60958. if (!is_object($info)) {
  60959. return $this->addPackage($info['package'], $info);
  60960. }
  60961. if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  60962. return $e;
  60963. }
  60964. $ret = $this->_addPackage2($info);
  60965. $this->_unlock();
  60966. if ($ret) {
  60967. $this->_dependencyDB->uninstallPackage($info);
  60968. $this->_dependencyDB->installPackage($info);
  60969. }
  60970. return $ret;
  60971. }
  60972. // }}}
  60973. // {{{ updateChannel()
  60974. /**
  60975. * For future expandibility purposes, separate this
  60976. * @param PEAR_ChannelFile
  60977. */
  60978. function updateChannel($channel, $lastmodified = null)
  60979. {
  60980. if ($channel->getName() == '__uri') {
  60981. return false;
  60982. }
  60983. return $this->addChannel($channel, $lastmodified, true);
  60984. }
  60985. // }}}
  60986. // {{{ deleteChannel()
  60987. /**
  60988. * Deletion fails if there are any packages installed from the channel
  60989. * @param string|PEAR_ChannelFile channel name
  60990. * @return boolean|PEAR_Error True on deletion, false if it doesn't exist
  60991. */
  60992. function deleteChannel($channel)
  60993. {
  60994. if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  60995. return $e;
  60996. }
  60997. $ret = $this->_deleteChannel($channel);
  60998. $this->_unlock();
  60999. if ($ret && is_a($this->_config, 'PEAR_Config')) {
  61000. $this->_config->setChannels($this->listChannels());
  61001. }
  61002. return $ret;
  61003. }
  61004. // }}}
  61005. // {{{ addChannel()
  61006. /**
  61007. * @param PEAR_ChannelFile Channel object
  61008. * @param string Last-Modified header from HTTP for caching
  61009. * @return boolean|PEAR_Error True on creation, false if it already exists
  61010. */
  61011. function addChannel($channel, $lastmodified = false, $update = false)
  61012. {
  61013. if (!is_a($channel, 'PEAR_ChannelFile') || !$channel->validate()) {
  61014. return false;
  61015. }
  61016. if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  61017. return $e;
  61018. }
  61019. $ret = $this->_addChannel($channel, $update, $lastmodified);
  61020. $this->_unlock();
  61021. if (!$update && $ret && is_a($this->_config, 'PEAR_Config')) {
  61022. $this->_config->setChannels($this->listChannels());
  61023. }
  61024. return $ret;
  61025. }
  61026. // }}}
  61027. // {{{ deletePackage()
  61028. function deletePackage($package, $channel = 'pear.php.net')
  61029. {
  61030. if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  61031. return $e;
  61032. }
  61033. $file = $this->_packageFileName($package, $channel);
  61034. $ret = file_exists($file) ? @unlink($file) : false;
  61035. $this->_rebuildFileMap();
  61036. $this->_unlock();
  61037. $p = array('channel' => $channel, 'package' => $package);
  61038. $this->_dependencyDB->uninstallPackage($p);
  61039. return $ret;
  61040. }
  61041. // }}}
  61042. // {{{ updatePackage()
  61043. function updatePackage($package, $info, $merge = true)
  61044. {
  61045. if (is_object($info)) {
  61046. return $this->updatePackage2($info, $merge);
  61047. }
  61048. if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  61049. return $e;
  61050. }
  61051. $ret = $this->_updatePackage($package, $info, $merge);
  61052. $this->_unlock();
  61053. if ($ret) {
  61054. if (!class_exists('PEAR_PackageFile_v1')) {
  61055. require_once 'PEAR/PackageFile/v1.php';
  61056. }
  61057. $pf = new PEAR_PackageFile_v1;
  61058. $pf->setConfig($this->_config);
  61059. $pf->fromArray($this->packageInfo($package));
  61060. $this->_dependencyDB->uninstallPackage($pf);
  61061. $this->_dependencyDB->installPackage($pf);
  61062. }
  61063. return $ret;
  61064. }
  61065. // }}}
  61066. // {{{ updatePackage2()
  61067. function updatePackage2($info)
  61068. {
  61069. if (!is_object($info)) {
  61070. return $this->updatePackage($info['package'], $info, $merge);
  61071. }
  61072. if (!$info->validate(PEAR_VALIDATE_DOWNLOADING)) {
  61073. return false;
  61074. }
  61075. if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  61076. return $e;
  61077. }
  61078. $ret = $this->_updatePackage2($info);
  61079. $this->_unlock();
  61080. if ($ret) {
  61081. $this->_dependencyDB->uninstallPackage($info);
  61082. $this->_dependencyDB->installPackage($info);
  61083. }
  61084. return $ret;
  61085. }
  61086. // }}}
  61087. // {{{ getChannel()
  61088. /**
  61089. * @param string channel name
  61090. * @param bool whether to strictly return raw channels (no aliases)
  61091. * @return PEAR_ChannelFile|PEAR_Error
  61092. */
  61093. function getChannel($channel, $noaliases = false)
  61094. {
  61095. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  61096. return $e;
  61097. }
  61098. $ret = $this->_getChannel($channel, $noaliases);
  61099. $this->_unlock();
  61100. if (!$ret) {
  61101. return PEAR::raiseError('Unknown channel: ' . $channel);
  61102. }
  61103. return $ret;
  61104. }
  61105. // }}}
  61106. // {{{ getPackage()
  61107. /**
  61108. * @param string package name
  61109. * @param string channel name
  61110. * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null
  61111. */
  61112. function &getPackage($package, $channel = 'pear.php.net')
  61113. {
  61114. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  61115. return $e;
  61116. }
  61117. $pf = &$this->_getPackage($package, $channel);
  61118. $this->_unlock();
  61119. return $pf;
  61120. }
  61121. // }}}
  61122. /**
  61123. * Get PEAR_PackageFile_v[1/2] objects representing the contents of
  61124. * a dependency group that are installed.
  61125. *
  61126. * This is used at uninstall-time
  61127. * @param array
  61128. * @return array|false
  61129. */
  61130. function getInstalledGroup($group)
  61131. {
  61132. $ret = array();
  61133. if (isset($group['package'])) {
  61134. if (!isset($group['package'][0])) {
  61135. $group['package'] = array($group['package']);
  61136. }
  61137. foreach ($group['package'] as $package) {
  61138. $depchannel = isset($package['channel']) ? $package['channel'] : '__uri';
  61139. $p = &$this->getPackage($package['name'], $depchannel);
  61140. if ($p) {
  61141. $save = &$p;
  61142. $ret[] = &$save;
  61143. }
  61144. }
  61145. }
  61146. if (isset($group['subpackage'])) {
  61147. if (!isset($group['subpackage'][0])) {
  61148. $group['subpackage'] = array($group['subpackage']);
  61149. }
  61150. foreach ($group['subpackage'] as $package) {
  61151. $depchannel = isset($package['channel']) ? $package['channel'] : '__uri';
  61152. $p = &$this->getPackage($package['name'], $depchannel);
  61153. if ($p) {
  61154. $save = &$p;
  61155. $ret[] = &$save;
  61156. }
  61157. }
  61158. }
  61159. if (!count($ret)) {
  61160. return false;
  61161. }
  61162. return $ret;
  61163. }
  61164. // {{{ getChannelValidator()
  61165. /**
  61166. * @param string channel name
  61167. * @return PEAR_Validate|false
  61168. */
  61169. function &getChannelValidator($channel)
  61170. {
  61171. $chan = $this->getChannel($channel);
  61172. if (PEAR::isError($chan)) {
  61173. return $chan;
  61174. }
  61175. $val = $chan->getValidationObject();
  61176. return $val;
  61177. }
  61178. // }}}
  61179. // {{{ getChannels()
  61180. /**
  61181. * @param string channel name
  61182. * @return array an array of PEAR_ChannelFile objects representing every installed channel
  61183. */
  61184. function &getChannels()
  61185. {
  61186. $ret = array();
  61187. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  61188. return $e;
  61189. }
  61190. foreach ($this->_listChannels() as $channel) {
  61191. $e = &$this->_getChannel($channel);
  61192. if (!$e || PEAR::isError($e)) {
  61193. continue;
  61194. }
  61195. $ret[] = $e;
  61196. }
  61197. $this->_unlock();
  61198. return $ret;
  61199. }
  61200. // }}}
  61201. // {{{ checkFileMap()
  61202. /**
  61203. * Test whether a file or set of files belongs to a package.
  61204. *
  61205. * If an array is passed in
  61206. * @param string|array file path, absolute or relative to the pear
  61207. * install dir
  61208. * @param string|array name of PEAR package or array('package' => name, 'channel' =>
  61209. * channel) of a package that will be ignored
  61210. * @param string API version - 1.1 will exclude any files belonging to a package
  61211. * @param array private recursion variable
  61212. * @return array|false which package and channel the file belongs to, or an empty
  61213. * string if the file does not belong to an installed package,
  61214. * or belongs to the second parameter's package
  61215. */
  61216. function checkFileMap($path, $package = false, $api = '1.0', $attrs = false)
  61217. {
  61218. if (is_array($path)) {
  61219. static $notempty;
  61220. if (empty($notempty)) {
  61221. if (!class_exists('PEAR_Installer_Role')) {
  61222. require_once 'PEAR/Installer/Role.php';
  61223. }
  61224. $notempty = function($a) { return !empty($a); };
  61225. }
  61226. $package = is_array($package) ? array(strtolower($package[0]), strtolower($package[1]))
  61227. : strtolower($package);
  61228. $pkgs = array();
  61229. foreach ($path as $name => $attrs) {
  61230. if (is_array($attrs)) {
  61231. if (isset($attrs['install-as'])) {
  61232. $name = $attrs['install-as'];
  61233. }
  61234. if (!in_array($attrs['role'], PEAR_Installer_Role::getInstallableRoles())) {
  61235. // these are not installed
  61236. continue;
  61237. }
  61238. if (!in_array($attrs['role'], PEAR_Installer_Role::getBaseinstallRoles())) {
  61239. $attrs['baseinstalldir'] = is_array($package) ? $package[1] : $package;
  61240. }
  61241. if (isset($attrs['baseinstalldir'])) {
  61242. $name = $attrs['baseinstalldir'] . DIRECTORY_SEPARATOR . $name;
  61243. }
  61244. }
  61245. $pkgs[$name] = $this->checkFileMap($name, $package, $api, $attrs);
  61246. if (PEAR::isError($pkgs[$name])) {
  61247. return $pkgs[$name];
  61248. }
  61249. }
  61250. return array_filter($pkgs, $notempty);
  61251. }
  61252. if (empty($this->filemap_cache)) {
  61253. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  61254. return $e;
  61255. }
  61256. $err = $this->_readFileMap();
  61257. $this->_unlock();
  61258. if (PEAR::isError($err)) {
  61259. return $err;
  61260. }
  61261. }
  61262. if (!$attrs) {
  61263. $attrs = array('role' => 'php'); // any old call would be for PHP role only
  61264. }
  61265. if (isset($this->filemap_cache[$attrs['role']][$path])) {
  61266. if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) {
  61267. return false;
  61268. }
  61269. return $this->filemap_cache[$attrs['role']][$path];
  61270. }
  61271. $l = strlen($this->install_dir);
  61272. if (substr($path, 0, $l) == $this->install_dir) {
  61273. $path = preg_replace('!^'.DIRECTORY_SEPARATOR.'+!', '', substr($path, $l));
  61274. }
  61275. if (isset($this->filemap_cache[$attrs['role']][$path])) {
  61276. if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) {
  61277. return false;
  61278. }
  61279. return $this->filemap_cache[$attrs['role']][$path];
  61280. }
  61281. return false;
  61282. }
  61283. // }}}
  61284. // {{{ flush()
  61285. /**
  61286. * Force a reload of the filemap
  61287. * @since 1.5.0RC3
  61288. */
  61289. function flushFileMap()
  61290. {
  61291. $this->filemap_cache = null;
  61292. clearstatcache(); // ensure that the next read gets the full, current filemap
  61293. }
  61294. // }}}
  61295. // {{{ apiVersion()
  61296. /**
  61297. * Get the expected API version. Channels API is version 1.1, as it is backwards
  61298. * compatible with 1.0
  61299. * @return string
  61300. */
  61301. function apiVersion()
  61302. {
  61303. return '1.1';
  61304. }
  61305. // }}}
  61306. /**
  61307. * Parse a package name, or validate a parsed package name array
  61308. * @param string|array pass in an array of format
  61309. * array(
  61310. * 'package' => 'pname',
  61311. * ['channel' => 'channame',]
  61312. * ['version' => 'version',]
  61313. * ['state' => 'state',]
  61314. * ['group' => 'groupname'])
  61315. * or a string of format
  61316. * [channel://][channame/]pname[-version|-state][/group=groupname]
  61317. * @return array|PEAR_Error
  61318. */
  61319. function parsePackageName($param, $defaultchannel = 'pear.php.net')
  61320. {
  61321. $saveparam = $param;
  61322. if (is_array($param)) {
  61323. // convert to string for error messages
  61324. $saveparam = $this->parsedPackageNameToString($param);
  61325. // process the array
  61326. if (!isset($param['package'])) {
  61327. return PEAR::raiseError('parsePackageName(): array $param ' .
  61328. 'must contain a valid package name in index "param"',
  61329. 'package', null, null, $param);
  61330. }
  61331. if (!isset($param['uri'])) {
  61332. if (!isset($param['channel'])) {
  61333. $param['channel'] = $defaultchannel;
  61334. }
  61335. } else {
  61336. $param['channel'] = '__uri';
  61337. }
  61338. } else {
  61339. $components = @parse_url((string) $param);
  61340. if (isset($components['scheme'])) {
  61341. if ($components['scheme'] == 'http') {
  61342. // uri package
  61343. $param = array('uri' => $param, 'channel' => '__uri');
  61344. } elseif($components['scheme'] != 'channel') {
  61345. return PEAR::raiseError('parsePackageName(): only channel:// uris may ' .
  61346. 'be downloaded, not "' . $param . '"', 'invalid', null, null, $param);
  61347. }
  61348. }
  61349. if (!isset($components['path'])) {
  61350. return PEAR::raiseError('parsePackageName(): array $param ' .
  61351. 'must contain a valid package name in "' . $param . '"',
  61352. 'package', null, null, $param);
  61353. }
  61354. if (isset($components['host'])) {
  61355. // remove the leading "/"
  61356. $components['path'] = substr($components['path'], 1);
  61357. }
  61358. if (!isset($components['scheme'])) {
  61359. if (strpos($components['path'], '/') !== false) {
  61360. if ($components['path'][0] == '/') {
  61361. return PEAR::raiseError('parsePackageName(): this is not ' .
  61362. 'a package name, it begins with "/" in "' . $param . '"',
  61363. 'invalid', null, null, $param);
  61364. }
  61365. $parts = explode('/', $components['path']);
  61366. $components['host'] = array_shift($parts);
  61367. if (count($parts) > 1) {
  61368. $components['path'] = array_pop($parts);
  61369. $components['host'] .= '/' . implode('/', $parts);
  61370. } else {
  61371. $components['path'] = implode('/', $parts);
  61372. }
  61373. } else {
  61374. $components['host'] = $defaultchannel;
  61375. }
  61376. } else {
  61377. if (strpos($components['path'], '/')) {
  61378. $parts = explode('/', $components['path']);
  61379. $components['path'] = array_pop($parts);
  61380. $components['host'] .= '/' . implode('/', $parts);
  61381. }
  61382. }
  61383. if (is_array($param)) {
  61384. $param['package'] = $components['path'];
  61385. } else {
  61386. $param = array(
  61387. 'package' => $components['path']
  61388. );
  61389. if (isset($components['host'])) {
  61390. $param['channel'] = $components['host'];
  61391. }
  61392. }
  61393. if (isset($components['fragment'])) {
  61394. $param['group'] = $components['fragment'];
  61395. }
  61396. if (isset($components['user'])) {
  61397. $param['user'] = $components['user'];
  61398. }
  61399. if (isset($components['pass'])) {
  61400. $param['pass'] = $components['pass'];
  61401. }
  61402. if (isset($components['query'])) {
  61403. parse_str($components['query'], $param['opts']);
  61404. }
  61405. // check for extension
  61406. $pathinfo = pathinfo($param['package']);
  61407. if (isset($pathinfo['extension']) &&
  61408. in_array(strtolower($pathinfo['extension']), array('tgz', 'tar'))) {
  61409. $param['extension'] = $pathinfo['extension'];
  61410. $param['package'] = substr($pathinfo['basename'], 0,
  61411. strlen($pathinfo['basename']) - 4);
  61412. }
  61413. // check for version
  61414. if (strpos($param['package'], '-')) {
  61415. $test = explode('-', $param['package']);
  61416. if (count($test) != 2) {
  61417. return PEAR::raiseError('parsePackageName(): only one version/state ' .
  61418. 'delimiter "-" is allowed in "' . $saveparam . '"',
  61419. 'version', null, null, $param);
  61420. }
  61421. list($param['package'], $param['version']) = $test;
  61422. }
  61423. }
  61424. // validation
  61425. $info = $this->channelExists($param['channel']);
  61426. if (PEAR::isError($info)) {
  61427. return $info;
  61428. }
  61429. if (!$info) {
  61430. return PEAR::raiseError('unknown channel "' . $param['channel'] .
  61431. '" in "' . $saveparam . '"', 'channel', null, null, $param);
  61432. }
  61433. $chan = $this->getChannel($param['channel']);
  61434. if (PEAR::isError($chan)) {
  61435. return $chan;
  61436. }
  61437. if (!$chan) {
  61438. return PEAR::raiseError("Exception: corrupt registry, could not " .
  61439. "retrieve channel " . $param['channel'] . " information",
  61440. 'registry', null, null, $param);
  61441. }
  61442. $param['channel'] = $chan->getName();
  61443. $validate = $chan->getValidationObject();
  61444. $vpackage = $chan->getValidationPackage();
  61445. // validate package name
  61446. if (!$validate->validPackageName($param['package'], $vpackage['_content'])) {
  61447. return PEAR::raiseError('parsePackageName(): invalid package name "' .
  61448. $param['package'] . '" in "' . $saveparam . '"',
  61449. 'package', null, null, $param);
  61450. }
  61451. if (isset($param['group'])) {
  61452. if (!PEAR_Validate::validGroupName($param['group'])) {
  61453. return PEAR::raiseError('parsePackageName(): dependency group "' . $param['group'] .
  61454. '" is not a valid group name in "' . $saveparam . '"', 'group', null, null,
  61455. $param);
  61456. }
  61457. }
  61458. if (isset($param['state'])) {
  61459. if (!in_array(strtolower($param['state']), $validate->getValidStates())) {
  61460. return PEAR::raiseError('parsePackageName(): state "' . $param['state']
  61461. . '" is not a valid state in "' . $saveparam . '"',
  61462. 'state', null, null, $param);
  61463. }
  61464. }
  61465. if (isset($param['version'])) {
  61466. if (isset($param['state'])) {
  61467. return PEAR::raiseError('parsePackageName(): cannot contain both ' .
  61468. 'a version and a stability (state) in "' . $saveparam . '"',
  61469. 'version/state', null, null, $param);
  61470. }
  61471. // check whether version is actually a state
  61472. if (in_array(strtolower($param['version']), $validate->getValidStates())) {
  61473. $param['state'] = strtolower($param['version']);
  61474. unset($param['version']);
  61475. } else {
  61476. if (!$validate->validVersion($param['version'])) {
  61477. return PEAR::raiseError('parsePackageName(): "' . $param['version'] .
  61478. '" is neither a valid version nor a valid state in "' .
  61479. $saveparam . '"', 'version/state', null, null, $param);
  61480. }
  61481. }
  61482. }
  61483. return $param;
  61484. }
  61485. /**
  61486. * @param array
  61487. * @return string
  61488. */
  61489. function parsedPackageNameToString($parsed, $brief = false)
  61490. {
  61491. if (is_string($parsed)) {
  61492. return $parsed;
  61493. }
  61494. if (is_object($parsed)) {
  61495. $p = $parsed;
  61496. $parsed = array(
  61497. 'package' => $p->getPackage(),
  61498. 'channel' => $p->getChannel(),
  61499. 'version' => $p->getVersion(),
  61500. );
  61501. }
  61502. if (isset($parsed['uri'])) {
  61503. return $parsed['uri'];
  61504. }
  61505. if ($brief) {
  61506. if ($channel = $this->channelAlias($parsed['channel'])) {
  61507. return $channel . '/' . $parsed['package'];
  61508. }
  61509. }
  61510. $upass = '';
  61511. if (isset($parsed['user'])) {
  61512. $upass = $parsed['user'];
  61513. if (isset($parsed['pass'])) {
  61514. $upass .= ':' . $parsed['pass'];
  61515. }
  61516. $upass = "$upass@";
  61517. }
  61518. $ret = 'channel://' . $upass . $parsed['channel'] . '/' . $parsed['package'];
  61519. if (isset($parsed['version']) || isset($parsed['state'])) {
  61520. $ver = isset($parsed['version']) ? $parsed['version'] : '';
  61521. $ver .= isset($parsed['state']) ? $parsed['state'] : '';
  61522. $ret .= '-' . $ver;
  61523. }
  61524. if (isset($parsed['extension'])) {
  61525. $ret .= '.' . $parsed['extension'];
  61526. }
  61527. if (isset($parsed['opts'])) {
  61528. $ret .= '?';
  61529. foreach ($parsed['opts'] as $name => $value) {
  61530. $parsed['opts'][$name] = "$name=$value";
  61531. }
  61532. $ret .= implode('&', $parsed['opts']);
  61533. }
  61534. if (isset($parsed['group'])) {
  61535. $ret .= '#' . $parsed['group'];
  61536. }
  61537. return $ret;
  61538. }
  61539. }
  61540. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/REST.php��������������������������������������������������������������������������0000664�0001750�0001750�00000040505�14720722517�014311� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  61541. /**
  61542. * PEAR_REST
  61543. *
  61544. * PHP versions 4 and 5
  61545. *
  61546. * @category pear
  61547. * @package PEAR
  61548. * @author Greg Beaver <cellog@php.net>
  61549. * @copyright 1997-2009 The Authors
  61550. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  61551. * @link http://pear.php.net/package/PEAR
  61552. * @since File available since Release 1.4.0a1
  61553. */
  61554. /**
  61555. * For downloading xml files
  61556. */
  61557. require_once 'PEAR.php';
  61558. require_once 'PEAR/XMLParser.php';
  61559. require_once 'PEAR/Proxy.php';
  61560. /**
  61561. * Intelligently retrieve data, following hyperlinks if necessary, and re-directing
  61562. * as well
  61563. * @category pear
  61564. * @package PEAR
  61565. * @author Greg Beaver <cellog@php.net>
  61566. * @copyright 1997-2009 The Authors
  61567. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  61568. * @version Release: 1.10.16
  61569. * @link http://pear.php.net/package/PEAR
  61570. * @since Class available since Release 1.4.0a1
  61571. */
  61572. class PEAR_REST
  61573. {
  61574. var $config;
  61575. var $_options;
  61576. function __construct(&$config, $options = array())
  61577. {
  61578. $this->config = &$config;
  61579. $this->_options = $options;
  61580. }
  61581. /**
  61582. * Retrieve REST data, but always retrieve the local cache if it is available.
  61583. *
  61584. * This is useful for elements that should never change, such as information on a particular
  61585. * release
  61586. * @param string full URL to this resource
  61587. * @param array|false contents of the accept-encoding header
  61588. * @param boolean if true, xml will be returned as a string, otherwise, xml will be
  61589. * parsed using PEAR_XMLParser
  61590. * @return string|array
  61591. */
  61592. function retrieveCacheFirst($url, $accept = false, $forcestring = false, $channel = false)
  61593. {
  61594. $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
  61595. md5($url) . 'rest.cachefile';
  61596. if (file_exists($cachefile)) {
  61597. return unserialize(implode('', file($cachefile)));
  61598. }
  61599. return $this->retrieveData($url, $accept, $forcestring, $channel);
  61600. }
  61601. /**
  61602. * Retrieve a remote REST resource
  61603. * @param string full URL to this resource
  61604. * @param array|false contents of the accept-encoding header
  61605. * @param boolean if true, xml will be returned as a string, otherwise, xml will be
  61606. * parsed using PEAR_XMLParser
  61607. * @return string|array
  61608. */
  61609. function retrieveData($url, $accept = false, $forcestring = false, $channel = false)
  61610. {
  61611. $cacheId = $this->getCacheId($url);
  61612. if ($ret = $this->useLocalCache($url, $cacheId)) {
  61613. return $ret;
  61614. }
  61615. $file = $trieddownload = false;
  61616. if (!isset($this->_options['offline'])) {
  61617. $trieddownload = true;
  61618. $file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false, $accept, $channel);
  61619. }
  61620. if (PEAR::isError($file)) {
  61621. if ($file->getCode() !== -9276) {
  61622. return $file;
  61623. }
  61624. $trieddownload = false;
  61625. $file = false; // use local copy if available on socket connect error
  61626. }
  61627. if (!$file) {
  61628. $ret = $this->getCache($url);
  61629. if (!PEAR::isError($ret) && $trieddownload) {
  61630. // reset the age of the cache if the server says it was unmodified
  61631. $result = $this->saveCache($url, $ret, null, true, $cacheId);
  61632. if (PEAR::isError($result)) {
  61633. return PEAR::raiseError($result->getMessage());
  61634. }
  61635. }
  61636. return $ret;
  61637. }
  61638. if (is_array($file)) {
  61639. $headers = $file[2];
  61640. $lastmodified = $file[1];
  61641. $content = $file[0];
  61642. } else {
  61643. $headers = array();
  61644. $lastmodified = false;
  61645. $content = $file;
  61646. }
  61647. if ($forcestring) {
  61648. $result = $this->saveCache($url, $content, $lastmodified, false, $cacheId);
  61649. if (PEAR::isError($result)) {
  61650. return PEAR::raiseError($result->getMessage());
  61651. }
  61652. return $content;
  61653. }
  61654. if (isset($headers['content-type'])) {
  61655. $content_type = explode(";", $headers['content-type']);
  61656. $content_type = $content_type[0];
  61657. switch ($content_type) {
  61658. case 'text/xml' :
  61659. case 'application/xml' :
  61660. case 'text/plain' :
  61661. if ($content_type === 'text/plain') {
  61662. $check = substr($content, 0, 5);
  61663. if ($check !== '<?xml') {
  61664. break;
  61665. }
  61666. }
  61667. $parser = new PEAR_XMLParser;
  61668. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  61669. $err = $parser->parse($content);
  61670. PEAR::popErrorHandling();
  61671. if (PEAR::isError($err)) {
  61672. return PEAR::raiseError('Invalid xml downloaded from "' . $url . '": ' .
  61673. $err->getMessage());
  61674. }
  61675. $content = $parser->getData();
  61676. case 'text/html' :
  61677. default :
  61678. // use it as a string
  61679. }
  61680. } else {
  61681. // assume XML
  61682. $parser = new PEAR_XMLParser;
  61683. $parser->parse($content);
  61684. $content = $parser->getData();
  61685. }
  61686. $result = $this->saveCache($url, $content, $lastmodified, false, $cacheId);
  61687. if (PEAR::isError($result)) {
  61688. return PEAR::raiseError($result->getMessage());
  61689. }
  61690. return $content;
  61691. }
  61692. function useLocalCache($url, $cacheid = null)
  61693. {
  61694. if (!is_array($cacheid)) {
  61695. $cacheid = $this->getCacheId($url);
  61696. }
  61697. $cachettl = $this->config->get('cache_ttl');
  61698. // If cache is newer than $cachettl seconds, we use the cache!
  61699. if (is_array($cacheid) && time() - $cacheid['age'] < $cachettl) {
  61700. return $this->getCache($url);
  61701. }
  61702. return false;
  61703. }
  61704. /**
  61705. * @param string $url
  61706. *
  61707. * @return bool|mixed
  61708. */
  61709. function getCacheId($url)
  61710. {
  61711. $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
  61712. md5($url) . 'rest.cacheid';
  61713. if (!file_exists($cacheidfile)) {
  61714. return false;
  61715. }
  61716. $ret = unserialize(implode('', file($cacheidfile)));
  61717. return $ret;
  61718. }
  61719. function getCache($url)
  61720. {
  61721. $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
  61722. md5($url) . 'rest.cachefile';
  61723. if (!file_exists($cachefile)) {
  61724. return PEAR::raiseError('No cached content available for "' . $url . '"');
  61725. }
  61726. return unserialize(implode('', file($cachefile)));
  61727. }
  61728. /**
  61729. * @param string full URL to REST resource
  61730. * @param string original contents of the REST resource
  61731. * @param array HTTP Last-Modified and ETag headers
  61732. * @param bool if true, then the cache id file should be regenerated to
  61733. * trigger a new time-to-live value
  61734. */
  61735. function saveCache($url, $contents, $lastmodified, $nochange = false, $cacheid = null)
  61736. {
  61737. $cache_dir = $this->config->get('cache_dir');
  61738. $d = $cache_dir . DIRECTORY_SEPARATOR . md5($url);
  61739. $cacheidfile = $d . 'rest.cacheid';
  61740. $cachefile = $d . 'rest.cachefile';
  61741. if (!is_dir($cache_dir)) {
  61742. if (System::mkdir(array('-p', $cache_dir)) === false) {
  61743. return PEAR::raiseError("The value of config option cache_dir ($cache_dir) is not a directory and attempts to create the directory failed.");
  61744. }
  61745. }
  61746. if (!is_writeable($cache_dir)) {
  61747. // If writing to the cache dir is not going to work, silently do nothing.
  61748. // An ugly hack, but retains compat with PEAR 1.9.1 where many commands
  61749. // work fine as non-root user (w/out write access to default cache dir).
  61750. return true;
  61751. }
  61752. if ($cacheid === null && $nochange) {
  61753. $cacheid = unserialize(implode('', file($cacheidfile)));
  61754. }
  61755. $idData = serialize(array(
  61756. 'age' => time(),
  61757. 'lastChange' => ($nochange ? $cacheid['lastChange'] : $lastmodified),
  61758. ));
  61759. $result = $this->saveCacheFile($cacheidfile, $idData);
  61760. if (PEAR::isError($result)) {
  61761. return $result;
  61762. } elseif ($nochange) {
  61763. return true;
  61764. }
  61765. $result = $this->saveCacheFile($cachefile, serialize($contents));
  61766. if (PEAR::isError($result)) {
  61767. if (file_exists($cacheidfile)) {
  61768. @unlink($cacheidfile);
  61769. }
  61770. return $result;
  61771. }
  61772. return true;
  61773. }
  61774. function saveCacheFile($file, $contents)
  61775. {
  61776. $len = strlen($contents);
  61777. $cachefile_fp = @fopen($file, 'xb'); // x is the O_CREAT|O_EXCL mode
  61778. if ($cachefile_fp !== false) { // create file
  61779. if (fwrite($cachefile_fp, $contents, $len) < $len) {
  61780. fclose($cachefile_fp);
  61781. return PEAR::raiseError("Could not write $file.");
  61782. }
  61783. } else { // update file
  61784. $cachefile_fp = @fopen($file, 'r+b'); // do not truncate file
  61785. if (!$cachefile_fp) {
  61786. return PEAR::raiseError("Could not open $file for writing.");
  61787. }
  61788. if (OS_WINDOWS) {
  61789. $not_symlink = !is_link($file); // see bug #18834
  61790. } else {
  61791. $cachefile_lstat = lstat($file);
  61792. $cachefile_fstat = fstat($cachefile_fp);
  61793. $not_symlink = $cachefile_lstat['mode'] == $cachefile_fstat['mode']
  61794. && $cachefile_lstat['ino'] == $cachefile_fstat['ino']
  61795. && $cachefile_lstat['dev'] == $cachefile_fstat['dev']
  61796. && $cachefile_fstat['nlink'] === 1;
  61797. }
  61798. if ($not_symlink) {
  61799. ftruncate($cachefile_fp, 0); // NOW truncate
  61800. if (fwrite($cachefile_fp, $contents, $len) < $len) {
  61801. fclose($cachefile_fp);
  61802. return PEAR::raiseError("Could not write $file.");
  61803. }
  61804. } else {
  61805. fclose($cachefile_fp);
  61806. $link = function_exists('readlink') ? readlink($file) : $file;
  61807. return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $file . ' as it is symlinked to ' . $link . ' - Possible symlink attack');
  61808. }
  61809. }
  61810. fclose($cachefile_fp);
  61811. return true;
  61812. }
  61813. /**
  61814. * Efficiently Download a file through HTTP. Returns downloaded file as a string in-memory
  61815. * This is best used for small files
  61816. *
  61817. * If an HTTP proxy has been configured (http_proxy PEAR_Config
  61818. * setting), the proxy will be used.
  61819. *
  61820. * @param string $url the URL to download
  61821. * @param string $save_dir directory to save file in
  61822. * @param false|string|array $lastmodified header values to check against for caching
  61823. * use false to return the header values from this download
  61824. * @param false|array $accept Accept headers to send
  61825. * @return string|array Returns the contents of the downloaded file or a PEAR
  61826. * error on failure. If the error is caused by
  61827. * socket-related errors, the error object will
  61828. * have the fsockopen error code available through
  61829. * getCode(). If caching is requested, then return the header
  61830. * values.
  61831. *
  61832. * @access public
  61833. */
  61834. function downloadHttp($url, $lastmodified = null, $accept = false, $channel = false)
  61835. {
  61836. static $redirect = 0;
  61837. // always reset , so we are clean case of error
  61838. $wasredirect = $redirect;
  61839. $redirect = 0;
  61840. $info = parse_url($url);
  61841. if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) {
  61842. return PEAR::raiseError('Cannot download non-http URL "' . $url . '"');
  61843. }
  61844. if (!isset($info['host'])) {
  61845. return PEAR::raiseError('Cannot download from non-URL "' . $url . '"');
  61846. }
  61847. $host = isset($info['host']) ? $info['host'] : null;
  61848. $port = isset($info['port']) ? $info['port'] : null;
  61849. $path = isset($info['path']) ? $info['path'] : null;
  61850. $schema = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http';
  61851. $proxy = new PEAR_Proxy($this->config);
  61852. if (empty($port)) {
  61853. $port = (isset($info['scheme']) && $info['scheme'] == 'https') ? 443 : 80;
  61854. }
  61855. if ($proxy->isProxyConfigured() && $schema === 'http') {
  61856. $request = "GET $url HTTP/1.1\r\n";
  61857. } else {
  61858. $request = "GET $path HTTP/1.1\r\n";
  61859. }
  61860. $request .= "Host: $host\r\n";
  61861. $ifmodifiedsince = '';
  61862. if (is_array($lastmodified)) {
  61863. if (isset($lastmodified['Last-Modified'])) {
  61864. $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n";
  61865. }
  61866. if (isset($lastmodified['ETag'])) {
  61867. $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";
  61868. }
  61869. } else {
  61870. $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');
  61871. }
  61872. $request .= $ifmodifiedsince .
  61873. "User-Agent: PEAR/1.10.16/PHP/" . PHP_VERSION . "\r\n";
  61874. $username = $this->config->get('username', null, $channel);
  61875. $password = $this->config->get('password', null, $channel);
  61876. if ($username && $password) {
  61877. $tmp = base64_encode("$username:$password");
  61878. $request .= "Authorization: Basic $tmp\r\n";
  61879. }
  61880. $proxyAuth = $proxy->getProxyAuth();
  61881. if ($proxyAuth) {
  61882. $request .= 'Proxy-Authorization: Basic ' .
  61883. $proxyAuth . "\r\n";
  61884. }
  61885. if ($accept) {
  61886. $request .= 'Accept: ' . implode(', ', $accept) . "\r\n";
  61887. }
  61888. $request .= "Accept-Encoding:\r\n";
  61889. $request .= "Connection: close\r\n";
  61890. $request .= "\r\n";
  61891. $secure = ($schema == 'https');
  61892. $fp = $proxy->openSocket($host, $port, $secure);
  61893. if (PEAR::isError($fp)) {
  61894. return $fp;
  61895. }
  61896. fwrite($fp, $request);
  61897. $headers = array();
  61898. $reply = 0;
  61899. while ($line = trim(fgets($fp, 1024))) {
  61900. if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) {
  61901. $headers[strtolower($matches[1])] = trim($matches[2]);
  61902. } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
  61903. $reply = (int)$matches[1];
  61904. if ($reply == 304 && ($lastmodified || ($lastmodified === false))) {
  61905. return false;
  61906. }
  61907. if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) {
  61908. return PEAR::raiseError("File $schema://$host:$port$path not valid (received: $line)");
  61909. }
  61910. }
  61911. }
  61912. if ($reply != 200) {
  61913. if (!isset($headers['location'])) {
  61914. return PEAR::raiseError("File $schema://$host:$port$path not valid (redirected but no location)");
  61915. }
  61916. if ($wasredirect > 4) {
  61917. return PEAR::raiseError("File $schema://$host:$port$path not valid (redirection looped more than 5 times)");
  61918. }
  61919. $redirect = $wasredirect + 1;
  61920. return $this->downloadHttp($headers['location'], $lastmodified, $accept, $channel);
  61921. }
  61922. $length = isset($headers['content-length']) ? $headers['content-length'] : -1;
  61923. $data = '';
  61924. while ($chunk = @fread($fp, 8192)) {
  61925. $data .= $chunk;
  61926. }
  61927. fclose($fp);
  61928. if ($lastmodified === false || $lastmodified) {
  61929. if (isset($headers['etag'])) {
  61930. $lastmodified = array('ETag' => $headers['etag']);
  61931. }
  61932. if (isset($headers['last-modified'])) {
  61933. if (is_array($lastmodified)) {
  61934. $lastmodified['Last-Modified'] = $headers['last-modified'];
  61935. } else {
  61936. $lastmodified = $headers['last-modified'];
  61937. }
  61938. }
  61939. return array($data, $lastmodified, $headers);
  61940. }
  61941. return $data;
  61942. }
  61943. }
  61944. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/RunTest.php�����������������������������������������������������������������������0000664�0001750�0001750�00000106632�14720722517�015144� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  61945. /**
  61946. * PEAR_RunTest
  61947. *
  61948. * PHP versions 4 and 5
  61949. *
  61950. * @category pear
  61951. * @package PEAR
  61952. * @author Tomas V.V.Cox <cox@idecnet.com>
  61953. * @author Greg Beaver <cellog@php.net>
  61954. * @copyright 1997-2009 The Authors
  61955. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  61956. * @link http://pear.php.net/package/PEAR
  61957. * @since File available since Release 1.3.3
  61958. */
  61959. /**
  61960. * for error handling
  61961. */
  61962. require_once 'PEAR.php';
  61963. require_once 'PEAR/Config.php';
  61964. define('DETAILED', 1);
  61965. putenv("PHP_PEAR_RUNTESTS=1");
  61966. /**
  61967. * Simplified version of PHP's test suite
  61968. *
  61969. * Try it with:
  61970. *
  61971. * $ php -r 'include "../PEAR/RunTest.php"; $t=new PEAR_RunTest; $o=$t->run("./pear_system.phpt");print_r($o);'
  61972. *
  61973. *
  61974. * @category pear
  61975. * @package PEAR
  61976. * @author Tomas V.V.Cox <cox@idecnet.com>
  61977. * @author Greg Beaver <cellog@php.net>
  61978. * @copyright 1997-2009 The Authors
  61979. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  61980. * @version Release: 1.10.16
  61981. * @link http://pear.php.net/package/PEAR
  61982. * @since Class available since Release 1.3.3
  61983. */
  61984. class PEAR_RunTest
  61985. {
  61986. var $_headers = array();
  61987. var $_logger;
  61988. var $_options;
  61989. var $_php;
  61990. var $tests_count;
  61991. var $xdebug_loaded;
  61992. /**
  61993. * Saved value of php executable, used to reset $_php when we
  61994. * have a test that uses cgi
  61995. *
  61996. * @var unknown_type
  61997. */
  61998. var $_savephp;
  61999. var $ini_overwrites = array(
  62000. 'output_handler=',
  62001. 'open_basedir=',
  62002. 'disable_functions=',
  62003. 'output_buffering=Off',
  62004. 'display_errors=1',
  62005. 'log_errors=0',
  62006. 'html_errors=0',
  62007. 'report_memleaks=0',
  62008. 'report_zend_debug=0',
  62009. 'docref_root=',
  62010. 'docref_ext=.html',
  62011. 'error_prepend_string=',
  62012. 'error_append_string=',
  62013. 'auto_prepend_file=',
  62014. 'auto_append_file=',
  62015. 'xdebug.default_enable=0',
  62016. 'allow_url_fopen=1',
  62017. );
  62018. /**
  62019. * An object that supports the PEAR_Common->log() signature, or null
  62020. * @param PEAR_Common|null
  62021. */
  62022. function __construct($logger = null, $options = array())
  62023. {
  62024. if (!defined('E_DEPRECATED')) {
  62025. define('E_DEPRECATED', 0);
  62026. }
  62027. if (!defined('E_STRICT')) {
  62028. define('E_STRICT', 0);
  62029. }
  62030. $excluded_error_reporting = E_DEPRECATED;
  62031. if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 80400) {
  62032. $excluded_error_reporting |= E_STRICT;
  62033. }
  62034. $this->ini_overwrites[] = 'error_reporting=' . (E_ALL & ~$excluded_error_reporting);
  62035. if (is_null($logger)) {
  62036. require_once 'PEAR/Common.php';
  62037. $logger = new PEAR_Common;
  62038. }
  62039. $this->_logger = $logger;
  62040. $this->_options = $options;
  62041. $conf = &PEAR_Config::singleton();
  62042. $this->_php = $conf->get('php_bin');
  62043. }
  62044. /**
  62045. * Taken from php-src/run-tests.php
  62046. *
  62047. * @param string $commandline command name
  62048. * @param array $env
  62049. * @param string $stdin standard input to pass to the command
  62050. * @return unknown
  62051. */
  62052. function system_with_timeout($commandline, $env = null, $stdin = null)
  62053. {
  62054. $data = '';
  62055. $proc = proc_open($commandline, array(
  62056. 0 => array('pipe', 'r'),
  62057. 1 => array('pipe', 'w'),
  62058. 2 => array('pipe', 'w')
  62059. ), $pipes, null, $env, array('suppress_errors' => true));
  62060. if (!$proc) {
  62061. return false;
  62062. }
  62063. if (is_string($stdin)) {
  62064. fwrite($pipes[0], $stdin);
  62065. }
  62066. fclose($pipes[0]);
  62067. while (true) {
  62068. /* hide errors from interrupted syscalls */
  62069. $r = $pipes;
  62070. unset($r[0]);
  62071. $e = $w = [];
  62072. $n = @stream_select($r, $w, $e, 60);
  62073. if ($n === 0) {
  62074. /* timed out */
  62075. $data .= "\n ** ERROR: process timed out **\n";
  62076. proc_terminate($proc);
  62077. return array(1234567890, $data);
  62078. } else if ($n > 0) {
  62079. $line = fread($pipes[1], 8192);
  62080. if (strlen($line) == 0) {
  62081. /* EOF */
  62082. break;
  62083. }
  62084. $data .= $line;
  62085. }
  62086. }
  62087. if (function_exists('proc_get_status')) {
  62088. $stat = proc_get_status($proc);
  62089. if ($stat['signaled']) {
  62090. $data .= "\nTermsig=".$stat['stopsig'];
  62091. }
  62092. }
  62093. $code = proc_close($proc);
  62094. if (function_exists('proc_get_status')) {
  62095. $code = $stat['exitcode'];
  62096. }
  62097. return array($code, $data);
  62098. }
  62099. /**
  62100. * Turns a PHP INI string into an array
  62101. *
  62102. * Turns -d "include_path=/foo/bar" into this:
  62103. * array(
  62104. * 'include_path' => array(
  62105. * 'operator' => '-d',
  62106. * 'value' => '/foo/bar',
  62107. * )
  62108. * )
  62109. * Works both with quotes and without
  62110. *
  62111. * @param string an PHP INI string, -d "include_path=/foo/bar"
  62112. * @return array
  62113. */
  62114. function iniString2array($ini_string)
  62115. {
  62116. if (!$ini_string) {
  62117. return array();
  62118. }
  62119. $split = preg_split('/[\s]|=/', $ini_string, -1, PREG_SPLIT_NO_EMPTY);
  62120. $key = $split[1][0] == '"' ? substr($split[1], 1) : $split[1];
  62121. $value = $split[2][strlen($split[2]) - 1] == '"' ? substr($split[2], 0, -1) : $split[2];
  62122. // FIXME review if this is really the struct to go with
  62123. $array = array($key => array('operator' => $split[0], 'value' => $value));
  62124. return $array;
  62125. }
  62126. function settings2array($settings, $ini_settings)
  62127. {
  62128. foreach ($settings as $setting) {
  62129. if (strpos($setting, '=') !== false) {
  62130. $setting = explode('=', $setting, 2);
  62131. $name = trim(strtolower($setting[0]));
  62132. $value = trim($setting[1]);
  62133. $ini_settings[$name] = $value;
  62134. }
  62135. }
  62136. return $ini_settings;
  62137. }
  62138. function settings2params($ini_settings)
  62139. {
  62140. $settings = '';
  62141. foreach ($ini_settings as $name => $value) {
  62142. if (is_array($value)) {
  62143. $operator = $value['operator'];
  62144. $value = $value['value'];
  62145. } else {
  62146. $operator = '-d';
  62147. }
  62148. $value = addslashes($value);
  62149. $settings .= " $operator \"$name=$value\"";
  62150. }
  62151. return $settings;
  62152. }
  62153. function _preparePhpBin($php, $file, $ini_settings)
  62154. {
  62155. $file = escapeshellarg($file);
  62156. $cmd = $php . $ini_settings . ' -f ' . $file;
  62157. return $cmd;
  62158. }
  62159. function runPHPUnit($file, $ini_settings = '')
  62160. {
  62161. if (!file_exists($file) && file_exists(getcwd() . DIRECTORY_SEPARATOR . $file)) {
  62162. $file = realpath(getcwd() . DIRECTORY_SEPARATOR . $file);
  62163. } elseif (file_exists($file)) {
  62164. $file = realpath($file);
  62165. }
  62166. $cmd = $this->_preparePhpBin($this->_php, $file, $ini_settings);
  62167. if (isset($this->_logger)) {
  62168. $this->_logger->log(2, 'Running command "' . $cmd . '"');
  62169. }
  62170. $savedir = getcwd(); // in case the test moves us around
  62171. chdir(dirname($file));
  62172. echo `$cmd`;
  62173. chdir($savedir);
  62174. return 'PASSED'; // we have no way of knowing this information so assume passing
  62175. }
  62176. /**
  62177. * Runs an individual test case.
  62178. *
  62179. * @param string The filename of the test
  62180. * @param array|string INI settings to be applied to the test run
  62181. * @param integer Number what the current running test is of the
  62182. * whole test suite being runned.
  62183. *
  62184. * @return string|object Returns PASSED, WARNED, FAILED depending on how the
  62185. * test came out.
  62186. * PEAR Error when the tester it self fails
  62187. */
  62188. function run($file, $ini_settings = array(), $test_number = 1)
  62189. {
  62190. $this->_restorePHPBinary();
  62191. if (empty($this->_options['cgi'])) {
  62192. // try to see if php-cgi is in the path
  62193. $res = $this->system_with_timeout('php-cgi -v');
  62194. if (false !== $res && !(is_array($res) && in_array($res[0], array(-1, 127)))) {
  62195. $this->_options['cgi'] = 'php-cgi';
  62196. }
  62197. }
  62198. if (1 < $len = strlen($this->tests_count)) {
  62199. $test_number = str_pad($test_number, $len, ' ', STR_PAD_LEFT);
  62200. $test_nr = "[$test_number/$this->tests_count] ";
  62201. } else {
  62202. $test_nr = '';
  62203. }
  62204. $file = realpath($file);
  62205. $section_text = $this->_readFile($file);
  62206. if (PEAR::isError($section_text)) {
  62207. return $section_text;
  62208. }
  62209. if (isset($section_text['POST_RAW']) && isset($section_text['UPLOAD'])) {
  62210. return PEAR::raiseError("Cannot contain both POST_RAW and UPLOAD in test file: $file");
  62211. }
  62212. $cwd = getcwd();
  62213. $pass_options = '';
  62214. if (!empty($this->_options['ini'])) {
  62215. $pass_options = $this->_options['ini'];
  62216. }
  62217. if (is_string($ini_settings)) {
  62218. $ini_settings = $this->iniString2array($ini_settings);
  62219. }
  62220. $ini_settings = $this->settings2array($this->ini_overwrites, $ini_settings);
  62221. if ($section_text['INI']) {
  62222. if (strpos($section_text['INI'], '{PWD}') !== false) {
  62223. $section_text['INI'] = str_replace('{PWD}', dirname($file), $section_text['INI']);
  62224. }
  62225. $ini = preg_split( "/[\n\r]+/", $section_text['INI']);
  62226. $ini_settings = $this->settings2array($ini, $ini_settings);
  62227. }
  62228. $ini_settings = $this->settings2params($ini_settings);
  62229. $shortname = str_replace($cwd . DIRECTORY_SEPARATOR, '', $file);
  62230. $tested = trim($section_text['TEST']);
  62231. $tested.= !isset($this->_options['simple']) ? "[$shortname]" : ' ';
  62232. if (!empty($section_text['POST']) || !empty($section_text['POST_RAW']) ||
  62233. !empty($section_text['UPLOAD']) || !empty($section_text['GET']) ||
  62234. !empty($section_text['COOKIE']) || !empty($section_text['EXPECTHEADERS'])) {
  62235. if (empty($this->_options['cgi'])) {
  62236. if (!isset($this->_options['quiet'])) {
  62237. $this->_logger->log(0, "SKIP $test_nr$tested (reason: --cgi option needed for this test, type 'pear help run-tests')");
  62238. }
  62239. if (isset($this->_options['tapoutput'])) {
  62240. return array('ok', ' # skip --cgi option needed for this test, "pear help run-tests" for info');
  62241. }
  62242. return 'SKIPPED';
  62243. }
  62244. $this->_savePHPBinary();
  62245. $this->_php = $this->_options['cgi'];
  62246. }
  62247. $temp_dir = realpath(dirname($file));
  62248. $main_file_name = basename($file, 'phpt');
  62249. $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'diff';
  62250. $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'log';
  62251. $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'exp';
  62252. $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'out';
  62253. $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'mem';
  62254. $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'php';
  62255. $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'skip.php';
  62256. $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name.'clean.php';
  62257. $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.');
  62258. // unlink old test results
  62259. $this->_cleanupOldFiles($file);
  62260. // Check if test should be skipped.
  62261. $res = $this->_runSkipIf($section_text, $temp_skipif, $tested, $ini_settings);
  62262. if ($res == 'SKIPPED' || count($res) != 2) {
  62263. return $res;
  62264. }
  62265. $info = $res['info'];
  62266. $warn = $res['warn'];
  62267. // We've satisfied the preconditions - run the test!
  62268. if (isset($this->_options['coverage']) && $this->xdebug_loaded) {
  62269. $xdebug_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'xdebug';
  62270. $text = "\n" . 'function coverage_shutdown() {' .
  62271. "\n" . ' $xdebug = var_export(xdebug_get_code_coverage(), true);';
  62272. if (!function_exists('file_put_contents')) {
  62273. $text .= "\n" . ' $fh = fopen(\'' . $xdebug_file . '\', "wb");' .
  62274. "\n" . ' if ($fh !== false) {' .
  62275. "\n" . ' fwrite($fh, $xdebug);' .
  62276. "\n" . ' fclose($fh);' .
  62277. "\n" . ' }';
  62278. } else {
  62279. $text .= "\n" . ' file_put_contents(\'' . $xdebug_file . '\', $xdebug);';
  62280. }
  62281. // Workaround for http://pear.php.net/bugs/bug.php?id=17292
  62282. $lines = explode("\n", $section_text['FILE']);
  62283. $numLines = count($lines);
  62284. $namespace = '';
  62285. $coverage_shutdown = 'coverage_shutdown';
  62286. if (
  62287. substr($lines[0], 0, 2) == '<?' ||
  62288. substr($lines[0], 0, 5) == '<?php'
  62289. ) {
  62290. unset($lines[0]);
  62291. }
  62292. for ($i = 0; $i < $numLines; $i++) {
  62293. if (isset($lines[$i]) && substr($lines[$i], 0, 9) == 'namespace') {
  62294. $namespace = substr($lines[$i], 10, -1);
  62295. $coverage_shutdown = $namespace . '\\coverage_shutdown';
  62296. $namespace = "namespace " . $namespace . ";\n";
  62297. unset($lines[$i]);
  62298. break;
  62299. }
  62300. }
  62301. $text .= "\n xdebug_stop_code_coverage();" .
  62302. "\n" . '} // end coverage_shutdown()' .
  62303. "\n\n" . 'register_shutdown_function("' . $coverage_shutdown . '");';
  62304. $text .= "\n" . 'xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);' . "\n";
  62305. $this->save_text($temp_file, "<?php\n" . $namespace . $text . "\n" . implode("\n", $lines));
  62306. } else {
  62307. $this->save_text($temp_file, $section_text['FILE']);
  62308. }
  62309. $args = $section_text['ARGS'] ? ' -- '.$section_text['ARGS'] : '';
  62310. $cmd = $this->_preparePhpBin($this->_php, $temp_file, $ini_settings);
  62311. $cmd.= "$args 2>&1";
  62312. if (isset($this->_logger)) {
  62313. $this->_logger->log(2, 'Running command "' . $cmd . '"');
  62314. }
  62315. // Reset environment from any previous test.
  62316. $env = $this->_resetEnv($section_text, $temp_file);
  62317. $section_text = $this->_processUpload($section_text, $file);
  62318. if (PEAR::isError($section_text)) {
  62319. return $section_text;
  62320. }
  62321. if (array_key_exists('POST_RAW', $section_text) && !empty($section_text['POST_RAW'])) {
  62322. $post = trim($section_text['POST_RAW']);
  62323. $raw_lines = explode("\n", $post);
  62324. $request = '';
  62325. $started = false;
  62326. foreach ($raw_lines as $i => $line) {
  62327. if (empty($env['CONTENT_TYPE']) &&
  62328. preg_match('/^Content-Type:(.*)/i', $line, $res)) {
  62329. $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1]));
  62330. continue;
  62331. }
  62332. if ($started) {
  62333. $request .= "\n";
  62334. }
  62335. $started = true;
  62336. $request .= $line;
  62337. }
  62338. $env['CONTENT_LENGTH'] = strlen($request);
  62339. $env['REQUEST_METHOD'] = 'POST';
  62340. $this->save_text($tmp_post, $request);
  62341. $cmd = "$this->_php$pass_options$ini_settings \"$temp_file\" 2>&1 < $tmp_post";
  62342. } elseif (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) {
  62343. $post = trim($section_text['POST']);
  62344. $this->save_text($tmp_post, $post);
  62345. $content_length = strlen($post);
  62346. $env['REQUEST_METHOD'] = 'POST';
  62347. $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
  62348. $env['CONTENT_LENGTH'] = $content_length;
  62349. $cmd = "$this->_php$pass_options$ini_settings \"$temp_file\" 2>&1 < $tmp_post";
  62350. } else {
  62351. $env['REQUEST_METHOD'] = 'GET';
  62352. $env['CONTENT_TYPE'] = '';
  62353. $env['CONTENT_LENGTH'] = '';
  62354. }
  62355. if (OS_WINDOWS && isset($section_text['RETURNS'])) {
  62356. ob_start();
  62357. system($cmd, $return_value);
  62358. $out = ob_get_contents();
  62359. ob_end_clean();
  62360. $section_text['RETURNS'] = (int) trim($section_text['RETURNS']);
  62361. $returnfail = ($return_value != $section_text['RETURNS']);
  62362. } else {
  62363. $returnfail = false;
  62364. $stdin = isset($section_text['STDIN']) ? $section_text['STDIN'] : null;
  62365. $out = $this->system_with_timeout($cmd, $env, $stdin);
  62366. $return_value = $out[0];
  62367. $out = $out[1];
  62368. }
  62369. $output = preg_replace('/\r\n/', "\n", trim($out));
  62370. if (isset($tmp_post) && realpath($tmp_post) && file_exists($tmp_post)) {
  62371. @unlink(realpath($tmp_post));
  62372. }
  62373. chdir($cwd); // in case the test moves us around
  62374. /* when using CGI, strip the headers from the output */
  62375. $output = $this->_stripHeadersCGI($output);
  62376. if (isset($section_text['EXPECTHEADERS'])) {
  62377. $testheaders = $this->_processHeaders($section_text['EXPECTHEADERS']);
  62378. $missing = array_diff_assoc($testheaders, $this->_headers);
  62379. $changed = '';
  62380. foreach ($missing as $header => $value) {
  62381. if (isset($this->_headers[$header])) {
  62382. $changed .= "-$header: $value\n+$header: ";
  62383. $changed .= $this->_headers[$header];
  62384. } else {
  62385. $changed .= "-$header: $value\n";
  62386. }
  62387. }
  62388. if ($missing) {
  62389. // tack on failed headers to output:
  62390. $output .= "\n====EXPECTHEADERS FAILURE====:\n$changed";
  62391. }
  62392. }
  62393. $this->_testCleanup($section_text, $temp_clean);
  62394. // Does the output match what is expected?
  62395. do {
  62396. if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) {
  62397. if (isset($section_text['EXPECTF'])) {
  62398. $wanted = trim($section_text['EXPECTF']);
  62399. } else {
  62400. $wanted = trim($section_text['EXPECTREGEX']);
  62401. }
  62402. $wanted_re = preg_replace('/\r\n/', "\n", $wanted);
  62403. if (isset($section_text['EXPECTF'])) {
  62404. $wanted_re = preg_quote($wanted_re, '/');
  62405. // Stick to basics
  62406. $wanted_re = str_replace("%s", ".+?", $wanted_re); //not greedy
  62407. $wanted_re = str_replace("%S", ".*?", $wanted_re); //not greedy
  62408. $wanted_re = str_replace("%i", "[+\-]?[0-9]+", $wanted_re);
  62409. $wanted_re = str_replace("%d", "[0-9]+", $wanted_re);
  62410. $wanted_re = str_replace("%x", "[0-9a-fA-F]+", $wanted_re);
  62411. $wanted_re = str_replace("%f", "[+\-]?\.?[0-9]+\.?[0-9]*(E-?[0-9]+)?", $wanted_re);
  62412. $wanted_re = str_replace("%c", ".", $wanted_re);
  62413. // %f allows two points "-.0.0" but that is the best *simple* expression
  62414. }
  62415. /* DEBUG YOUR REGEX HERE
  62416. var_dump($wanted_re);
  62417. print(str_repeat('=', 80) . "\n");
  62418. var_dump($output);
  62419. */
  62420. if (!$returnfail && preg_match("/^$wanted_re\$/s", $output)) {
  62421. if (file_exists($temp_file)) {
  62422. unlink($temp_file);
  62423. }
  62424. if (array_key_exists('FAIL', $section_text)) {
  62425. break;
  62426. }
  62427. if (!isset($this->_options['quiet'])) {
  62428. $this->_logger->log(0, "PASS $test_nr$tested$info");
  62429. }
  62430. if (isset($this->_options['tapoutput'])) {
  62431. return array('ok', ' - ' . $tested);
  62432. }
  62433. return 'PASSED';
  62434. }
  62435. } else {
  62436. if (isset($section_text['EXPECTFILE'])) {
  62437. $f = $temp_dir . '/' . trim($section_text['EXPECTFILE']);
  62438. if (!($fp = @fopen($f, 'rb'))) {
  62439. return PEAR::raiseError('--EXPECTFILE-- section file ' .
  62440. $f . ' not found');
  62441. }
  62442. fclose($fp);
  62443. $section_text['EXPECT'] = file_get_contents($f);
  62444. }
  62445. if (isset($section_text['EXPECT'])) {
  62446. $wanted = preg_replace('/\r\n/', "\n", trim($section_text['EXPECT']));
  62447. } else {
  62448. $wanted = '';
  62449. }
  62450. // compare and leave on success
  62451. if (!$returnfail && 0 == strcmp($output, $wanted)) {
  62452. if (file_exists($temp_file)) {
  62453. unlink($temp_file);
  62454. }
  62455. if (array_key_exists('FAIL', $section_text)) {
  62456. break;
  62457. }
  62458. if (!isset($this->_options['quiet'])) {
  62459. $this->_logger->log(0, "PASS $test_nr$tested$info");
  62460. }
  62461. if (isset($this->_options['tapoutput'])) {
  62462. return array('ok', ' - ' . $tested);
  62463. }
  62464. return 'PASSED';
  62465. }
  62466. }
  62467. } while (false);
  62468. if (array_key_exists('FAIL', $section_text)) {
  62469. // we expect a particular failure
  62470. // this is only used for testing PEAR_RunTest
  62471. $expectf = isset($section_text['EXPECTF']) ? $wanted_re : null;
  62472. $faildiff = $this->generate_diff($wanted, $output, null, $expectf);
  62473. $faildiff = preg_replace('/\r/', '', $faildiff);
  62474. $wanted = preg_replace('/\r/', '', trim($section_text['FAIL']));
  62475. if ($faildiff == $wanted) {
  62476. if (!isset($this->_options['quiet'])) {
  62477. $this->_logger->log(0, "PASS $test_nr$tested$info");
  62478. }
  62479. if (isset($this->_options['tapoutput'])) {
  62480. return array('ok', ' - ' . $tested);
  62481. }
  62482. return 'PASSED';
  62483. }
  62484. unset($section_text['EXPECTF']);
  62485. $output = $faildiff;
  62486. if (isset($section_text['RETURNS'])) {
  62487. return PEAR::raiseError('Cannot have both RETURNS and FAIL in the same test: ' .
  62488. $file);
  62489. }
  62490. }
  62491. // Test failed so we need to report details.
  62492. $txt = $warn ? 'WARN ' : 'FAIL ';
  62493. $this->_logger->log(0, $txt . $test_nr . $tested . $info);
  62494. // write .exp
  62495. $res = $this->_writeLog($exp_filename, $wanted);
  62496. if (PEAR::isError($res)) {
  62497. return $res;
  62498. }
  62499. // write .out
  62500. $res = $this->_writeLog($output_filename, $output);
  62501. if (PEAR::isError($res)) {
  62502. return $res;
  62503. }
  62504. // write .diff
  62505. $returns = isset($section_text['RETURNS']) ?
  62506. array(trim($section_text['RETURNS']), $return_value) : null;
  62507. $expectf = isset($section_text['EXPECTF']) ? $wanted_re : null;
  62508. $data = $this->generate_diff($wanted, $output, $returns, $expectf);
  62509. $res = $this->_writeLog($diff_filename, $data);
  62510. if (isset($this->_options['showdiff'])) {
  62511. $this->_logger->log(0, "========DIFF========");
  62512. $this->_logger->log(0, $data);
  62513. $this->_logger->log(0, "========DONE========");
  62514. }
  62515. if (PEAR::isError($res)) {
  62516. return $res;
  62517. }
  62518. // write .log
  62519. $data = "
  62520. ---- EXPECTED OUTPUT
  62521. $wanted
  62522. ---- ACTUAL OUTPUT
  62523. $output
  62524. ---- FAILED
  62525. ";
  62526. if ($returnfail) {
  62527. $data .= "
  62528. ---- EXPECTED RETURN
  62529. $section_text[RETURNS]
  62530. ---- ACTUAL RETURN
  62531. $return_value
  62532. ";
  62533. }
  62534. $res = $this->_writeLog($log_filename, $data);
  62535. if (PEAR::isError($res)) {
  62536. return $res;
  62537. }
  62538. if (isset($this->_options['tapoutput'])) {
  62539. $wanted = explode("\n", $wanted);
  62540. $wanted = "# Expected output:\n#\n#" . implode("\n#", $wanted);
  62541. $output = explode("\n", $output);
  62542. $output = "#\n#\n# Actual output:\n#\n#" . implode("\n#", $output);
  62543. return array($wanted . $output . 'not ok', ' - ' . $tested);
  62544. }
  62545. return $warn ? 'WARNED' : 'FAILED';
  62546. }
  62547. function generate_diff($wanted, $output, $rvalue, $wanted_re)
  62548. {
  62549. $w = explode("\n", $wanted);
  62550. $o = explode("\n", $output);
  62551. $wr = explode("\n", $wanted_re);
  62552. $w1 = array_diff_assoc($w, $o);
  62553. $o1 = array_diff_assoc($o, $w);
  62554. $o2 = $w2 = array();
  62555. foreach ($w1 as $idx => $val) {
  62556. if (!$wanted_re || !isset($wr[$idx]) || !isset($o1[$idx]) ||
  62557. !preg_match('/^' . $wr[$idx] . '\\z/', $o1[$idx])) {
  62558. $w2[sprintf("%03d<", $idx)] = sprintf("%03d- ", $idx + 1) . $val;
  62559. }
  62560. }
  62561. foreach ($o1 as $idx => $val) {
  62562. if (!$wanted_re || !isset($wr[$idx]) ||
  62563. !preg_match('/^' . $wr[$idx] . '\\z/', $val)) {
  62564. $o2[sprintf("%03d>", $idx)] = sprintf("%03d+ ", $idx + 1) . $val;
  62565. }
  62566. }
  62567. $diff = array_merge($w2, $o2);
  62568. ksort($diff);
  62569. $extra = $rvalue ? "##EXPECTED: $rvalue[0]\r\n##RETURNED: $rvalue[1]" : '';
  62570. return implode("\r\n", $diff) . $extra;
  62571. }
  62572. // Write the given text to a temporary file, and return the filename.
  62573. function save_text($filename, $text)
  62574. {
  62575. if (!$fp = fopen($filename, 'w')) {
  62576. return PEAR::raiseError("Cannot open file '" . $filename . "' (save_text)");
  62577. }
  62578. fwrite($fp, $text);
  62579. fclose($fp);
  62580. if (1 < DETAILED) echo "
  62581. FILE $filename {{{
  62582. $text
  62583. }}}
  62584. ";
  62585. }
  62586. function _cleanupOldFiles($file)
  62587. {
  62588. $temp_dir = realpath(dirname($file));
  62589. $mainFileName = basename($file, 'phpt');
  62590. $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'diff';
  62591. $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'log';
  62592. $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'exp';
  62593. $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'out';
  62594. $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'mem';
  62595. $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'php';
  62596. $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'skip.php';
  62597. $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $mainFileName.'clean.php';
  62598. $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . uniqid('phpt.');
  62599. // unlink old test results
  62600. @unlink($diff_filename);
  62601. @unlink($log_filename);
  62602. @unlink($exp_filename);
  62603. @unlink($output_filename);
  62604. @unlink($memcheck_filename);
  62605. @unlink($temp_file);
  62606. @unlink($temp_skipif);
  62607. @unlink($tmp_post);
  62608. @unlink($temp_clean);
  62609. }
  62610. function _runSkipIf($section_text, $temp_skipif, $tested, $ini_settings)
  62611. {
  62612. $info = '';
  62613. $warn = false;
  62614. if (array_key_exists('SKIPIF', $section_text) && trim($section_text['SKIPIF'])) {
  62615. $this->save_text($temp_skipif, $section_text['SKIPIF']);
  62616. $output = $this->system_with_timeout("$this->_php$ini_settings -f \"$temp_skipif\"");
  62617. $output = $output[1];
  62618. $loutput = ltrim($output);
  62619. unlink($temp_skipif);
  62620. if (!strncasecmp('skip', $loutput, 4)) {
  62621. $skipreason = "SKIP $tested";
  62622. if (preg_match('/^\s*skip\s*(.+)\s*/i', $output, $m)) {
  62623. $skipreason .= '(reason: ' . $m[1] . ')';
  62624. }
  62625. if (!isset($this->_options['quiet'])) {
  62626. $this->_logger->log(0, $skipreason);
  62627. }
  62628. if (isset($this->_options['tapoutput'])) {
  62629. return array('ok', ' # skip ' . $reason);
  62630. }
  62631. return 'SKIPPED';
  62632. }
  62633. if (!strncasecmp('info', $loutput, 4)
  62634. && preg_match('/^\s*info\s*(.+)\s*/i', $output, $m)) {
  62635. $info = " (info: $m[1])";
  62636. }
  62637. if (!strncasecmp('warn', $loutput, 4)
  62638. && preg_match('/^\s*warn\s*(.+)\s*/i', $output, $m)) {
  62639. $warn = true; /* only if there is a reason */
  62640. $info = " (warn: $m[1])";
  62641. }
  62642. }
  62643. return array('warn' => $warn, 'info' => $info);
  62644. }
  62645. function _stripHeadersCGI($output)
  62646. {
  62647. $this->headers = array();
  62648. if (!empty($this->_options['cgi']) &&
  62649. $this->_php == $this->_options['cgi'] &&
  62650. preg_match("/^(.*?)(?:\n\n(.*)|\\z)/s", $output, $match)) {
  62651. $output = isset($match[2]) ? trim($match[2]) : '';
  62652. $this->_headers = $this->_processHeaders($match[1]);
  62653. }
  62654. return $output;
  62655. }
  62656. /**
  62657. * Return an array that can be used with array_diff() to compare headers
  62658. *
  62659. * @param string $text
  62660. */
  62661. function _processHeaders($text)
  62662. {
  62663. $headers = array();
  62664. $rh = preg_split("/[\n\r]+/", $text);
  62665. foreach ($rh as $line) {
  62666. if (strpos($line, ':')!== false) {
  62667. $line = explode(':', $line, 2);
  62668. $headers[trim($line[0])] = trim($line[1]);
  62669. }
  62670. }
  62671. return $headers;
  62672. }
  62673. function _readFile($file)
  62674. {
  62675. // Load the sections of the test file.
  62676. $section_text = array(
  62677. 'TEST' => '(unnamed test)',
  62678. 'SKIPIF' => '',
  62679. 'GET' => '',
  62680. 'COOKIE' => '',
  62681. 'POST' => '',
  62682. 'ARGS' => '',
  62683. 'INI' => '',
  62684. 'CLEAN' => '',
  62685. );
  62686. if (!is_file($file) || !$fp = fopen($file, "r")) {
  62687. return PEAR::raiseError("Cannot open test file: $file");
  62688. }
  62689. $section = '';
  62690. while (!feof($fp)) {
  62691. $line = fgets($fp);
  62692. // Match the beginning of a section.
  62693. if (preg_match('/^--([_A-Z]+)--/', $line, $r)) {
  62694. $section = $r[1];
  62695. $section_text[$section] = '';
  62696. continue;
  62697. } elseif (empty($section)) {
  62698. fclose($fp);
  62699. return PEAR::raiseError("Invalid sections formats in test file: $file");
  62700. }
  62701. // Add to the section text.
  62702. $section_text[$section] .= $line;
  62703. }
  62704. fclose($fp);
  62705. return $section_text;
  62706. }
  62707. function _writeLog($logname, $data)
  62708. {
  62709. if (!$log = fopen($logname, 'w')) {
  62710. return PEAR::raiseError("Cannot create test log - $logname");
  62711. }
  62712. fwrite($log, $data);
  62713. fclose($log);
  62714. }
  62715. function _resetEnv($section_text, $temp_file)
  62716. {
  62717. $env = $_ENV;
  62718. $env['REDIRECT_STATUS'] = '';
  62719. $env['QUERY_STRING'] = '';
  62720. $env['PATH_TRANSLATED'] = '';
  62721. $env['SCRIPT_FILENAME'] = '';
  62722. $env['REQUEST_METHOD'] = '';
  62723. $env['CONTENT_TYPE'] = '';
  62724. $env['CONTENT_LENGTH'] = '';
  62725. if (!empty($section_text['ENV'])) {
  62726. if (strpos($section_text['ENV'], '{PWD}') !== false) {
  62727. $section_text['ENV'] = str_replace('{PWD}', dirname($temp_file), $section_text['ENV']);
  62728. }
  62729. foreach (explode("\n", trim($section_text['ENV'])) as $e) {
  62730. $e = explode('=', trim($e), 2);
  62731. if (!empty($e[0]) && isset($e[1])) {
  62732. $env[$e[0]] = $e[1];
  62733. }
  62734. }
  62735. }
  62736. if (array_key_exists('GET', $section_text)) {
  62737. $env['QUERY_STRING'] = trim($section_text['GET']);
  62738. } else {
  62739. $env['QUERY_STRING'] = '';
  62740. }
  62741. if (array_key_exists('COOKIE', $section_text)) {
  62742. $env['HTTP_COOKIE'] = trim($section_text['COOKIE']);
  62743. } else {
  62744. $env['HTTP_COOKIE'] = '';
  62745. }
  62746. $env['REDIRECT_STATUS'] = '1';
  62747. $env['PATH_TRANSLATED'] = $temp_file;
  62748. $env['SCRIPT_FILENAME'] = $temp_file;
  62749. return $env;
  62750. }
  62751. function _processUpload($section_text, $file)
  62752. {
  62753. if (array_key_exists('UPLOAD', $section_text) && !empty($section_text['UPLOAD'])) {
  62754. $upload_files = trim($section_text['UPLOAD']);
  62755. $upload_files = explode("\n", $upload_files);
  62756. $request = "Content-Type: multipart/form-data; boundary=---------------------------20896060251896012921717172737\n" .
  62757. "-----------------------------20896060251896012921717172737\n";
  62758. foreach ($upload_files as $fileinfo) {
  62759. $fileinfo = explode('=', $fileinfo);
  62760. if (count($fileinfo) != 2) {
  62761. return PEAR::raiseError("Invalid UPLOAD section in test file: $file");
  62762. }
  62763. if (!realpath(dirname($file) . '/' . $fileinfo[1])) {
  62764. return PEAR::raiseError("File for upload does not exist: $fileinfo[1] " .
  62765. "in test file: $file");
  62766. }
  62767. $file_contents = file_get_contents(dirname($file) . '/' . $fileinfo[1]);
  62768. $fileinfo[1] = basename($fileinfo[1]);
  62769. $request .= "Content-Disposition: form-data; name=\"$fileinfo[0]\"; filename=\"$fileinfo[1]\"\n";
  62770. $request .= "Content-Type: text/plain\n\n";
  62771. $request .= $file_contents . "\n" .
  62772. "-----------------------------20896060251896012921717172737\n";
  62773. }
  62774. if (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) {
  62775. // encode POST raw
  62776. $post = trim($section_text['POST']);
  62777. $post = explode('&', $post);
  62778. foreach ($post as $i => $post_info) {
  62779. $post_info = explode('=', $post_info);
  62780. if (count($post_info) != 2) {
  62781. return PEAR::raiseError("Invalid POST data in test file: $file");
  62782. }
  62783. $post_info[0] = rawurldecode($post_info[0]);
  62784. $post_info[1] = rawurldecode($post_info[1]);
  62785. $post[$i] = $post_info;
  62786. }
  62787. foreach ($post as $post_info) {
  62788. $request .= "Content-Disposition: form-data; name=\"$post_info[0]\"\n\n";
  62789. $request .= $post_info[1] . "\n" .
  62790. "-----------------------------20896060251896012921717172737\n";
  62791. }
  62792. unset($section_text['POST']);
  62793. }
  62794. $section_text['POST_RAW'] = $request;
  62795. }
  62796. return $section_text;
  62797. }
  62798. function _testCleanup($section_text, $temp_clean)
  62799. {
  62800. if ($section_text['CLEAN']) {
  62801. $this->_restorePHPBinary();
  62802. // perform test cleanup
  62803. $this->save_text($temp_clean, $section_text['CLEAN']);
  62804. $output = $this->system_with_timeout("$this->_php $temp_clean 2>&1");
  62805. if (strlen($output[1])) {
  62806. echo "BORKED --CLEAN-- section! output:\n", $output[1];
  62807. }
  62808. if (file_exists($temp_clean)) {
  62809. unlink($temp_clean);
  62810. }
  62811. }
  62812. }
  62813. function _savePHPBinary()
  62814. {
  62815. $this->_savephp = $this->_php;
  62816. }
  62817. function _restorePHPBinary()
  62818. {
  62819. if (isset($this->_savephp))
  62820. {
  62821. $this->_php = $this->_savephp;
  62822. unset($this->_savephp);
  62823. }
  62824. }
  62825. }
  62826. ������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR/Validate.php����������������������������������������������������������������������0000664�0001750�0001750�00000052765�14720722517�015300� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  62827. /**
  62828. * PEAR_Validate
  62829. *
  62830. * PHP versions 4 and 5
  62831. *
  62832. * @category pear
  62833. * @package PEAR
  62834. * @author Greg Beaver <cellog@php.net>
  62835. * @copyright 1997-2009 The Authors
  62836. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  62837. * @link http://pear.php.net/package/PEAR
  62838. * @since File available since Release 1.4.0a1
  62839. */
  62840. /**#@+
  62841. * Constants for install stage
  62842. */
  62843. define('PEAR_VALIDATE_INSTALLING', 1);
  62844. define('PEAR_VALIDATE_UNINSTALLING', 2); // this is not bit-mapped like the others
  62845. define('PEAR_VALIDATE_NORMAL', 3);
  62846. define('PEAR_VALIDATE_DOWNLOADING', 4); // this is not bit-mapped like the others
  62847. define('PEAR_VALIDATE_PACKAGING', 7);
  62848. /**#@-*/
  62849. require_once 'PEAR/Common.php';
  62850. require_once 'PEAR/Validator/PECL.php';
  62851. /**
  62852. * Validation class for package.xml - channel-level advanced validation
  62853. * @category pear
  62854. * @package PEAR
  62855. * @author Greg Beaver <cellog@php.net>
  62856. * @copyright 1997-2009 The Authors
  62857. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  62858. * @version Release: 1.10.16
  62859. * @link http://pear.php.net/package/PEAR
  62860. * @since Class available since Release 1.4.0a1
  62861. */
  62862. class PEAR_Validate
  62863. {
  62864. var $packageregex = _PEAR_COMMON_PACKAGE_NAME_PREG;
  62865. /**
  62866. * @var PEAR_PackageFile_v1|PEAR_PackageFile_v2
  62867. */
  62868. var $_packagexml;
  62869. /**
  62870. * @var int one of the PEAR_VALIDATE_* constants
  62871. */
  62872. var $_state = PEAR_VALIDATE_NORMAL;
  62873. /**
  62874. * Format: ('error' => array('field' => name, 'reason' => reason), 'warning' => same)
  62875. * @var array
  62876. * @access private
  62877. */
  62878. var $_failures = array('error' => array(), 'warning' => array());
  62879. /**
  62880. * Override this method to handle validation of normal package names
  62881. * @param string
  62882. * @return bool
  62883. * @access protected
  62884. */
  62885. function _validPackageName($name)
  62886. {
  62887. return (bool) preg_match('/^' . $this->packageregex . '\\z/', $name);
  62888. }
  62889. /**
  62890. * @param string package name to validate
  62891. * @param string name of channel-specific validation package
  62892. * @final
  62893. */
  62894. function validPackageName($name, $validatepackagename = false)
  62895. {
  62896. if ($validatepackagename) {
  62897. if (strtolower($name) == strtolower($validatepackagename)) {
  62898. return (bool) preg_match('/^[a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+)*\\z/', $name);
  62899. }
  62900. }
  62901. return $this->_validPackageName($name);
  62902. }
  62903. /**
  62904. * This validates a bundle name, and bundle names must conform
  62905. * to the PEAR naming convention, so the method is final and static.
  62906. * @param string
  62907. * @final
  62908. */
  62909. public static function validGroupName($name)
  62910. {
  62911. return (bool) preg_match('/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/', $name);
  62912. }
  62913. /**
  62914. * Determine whether $state represents a valid stability level
  62915. * @param string
  62916. * @return bool
  62917. * @final
  62918. */
  62919. public static function validState($state)
  62920. {
  62921. return in_array($state, array('snapshot', 'devel', 'alpha', 'beta', 'stable'));
  62922. }
  62923. /**
  62924. * Get a list of valid stability levels
  62925. * @return array
  62926. * @final
  62927. */
  62928. public static function getValidStates()
  62929. {
  62930. return array('snapshot', 'devel', 'alpha', 'beta', 'stable');
  62931. }
  62932. /**
  62933. * Determine whether a version is a properly formatted version number that can be used
  62934. * by version_compare
  62935. * @param string
  62936. * @return bool
  62937. * @final
  62938. */
  62939. public static function validVersion($ver)
  62940. {
  62941. return (bool) preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
  62942. }
  62943. /**
  62944. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  62945. */
  62946. function setPackageFile(&$pf)
  62947. {
  62948. $this->_packagexml = &$pf;
  62949. }
  62950. /**
  62951. * @access private
  62952. */
  62953. function _addFailure($field, $reason)
  62954. {
  62955. $this->_failures['errors'][] = array('field' => $field, 'reason' => $reason);
  62956. }
  62957. /**
  62958. * @access private
  62959. */
  62960. function _addWarning($field, $reason)
  62961. {
  62962. $this->_failures['warnings'][] = array('field' => $field, 'reason' => $reason);
  62963. }
  62964. function getFailures()
  62965. {
  62966. $failures = $this->_failures;
  62967. $this->_failures = array('warnings' => array(), 'errors' => array());
  62968. return $failures;
  62969. }
  62970. /**
  62971. * @param int one of the PEAR_VALIDATE_* constants
  62972. */
  62973. function validate($state = null)
  62974. {
  62975. if (!isset($this->_packagexml)) {
  62976. return false;
  62977. }
  62978. if ($state !== null) {
  62979. $this->_state = $state;
  62980. }
  62981. $this->_failures = array('warnings' => array(), 'errors' => array());
  62982. $this->validatePackageName();
  62983. $this->validateVersion();
  62984. $this->validateMaintainers();
  62985. $this->validateDate();
  62986. $this->validateSummary();
  62987. $this->validateDescription();
  62988. $this->validateLicense();
  62989. $this->validateNotes();
  62990. if ($this->_packagexml->getPackagexmlVersion() == '1.0') {
  62991. $this->validateState();
  62992. $this->validateFilelist();
  62993. } elseif ($this->_packagexml->getPackagexmlVersion() == '2.0' ||
  62994. $this->_packagexml->getPackagexmlVersion() == '2.1') {
  62995. $this->validateTime();
  62996. $this->validateStability();
  62997. $this->validateDeps();
  62998. $this->validateMainFilelist();
  62999. $this->validateReleaseFilelist();
  63000. //$this->validateGlobalTasks();
  63001. $this->validateChangelog();
  63002. }
  63003. return !((bool) count($this->_failures['errors']));
  63004. }
  63005. /**
  63006. * @access protected
  63007. */
  63008. function validatePackageName()
  63009. {
  63010. if ($this->_state == PEAR_VALIDATE_PACKAGING ||
  63011. $this->_state == PEAR_VALIDATE_NORMAL) {
  63012. if (($this->_packagexml->getPackagexmlVersion() == '2.0' ||
  63013. $this->_packagexml->getPackagexmlVersion() == '2.1') &&
  63014. $this->_packagexml->getExtends()) {
  63015. $version = $this->_packagexml->getVersion() . '';
  63016. $name = $this->_packagexml->getPackage();
  63017. $a = explode('.', $version);
  63018. $test = array_shift($a);
  63019. if ($test == '0') {
  63020. return true;
  63021. }
  63022. $vlen = strlen($test);
  63023. $majver = substr($name, strlen($name) - $vlen);
  63024. while ($majver && !is_numeric($majver[0])) {
  63025. $majver = substr($majver, 1);
  63026. }
  63027. if ($majver != $test) {
  63028. $this->_addWarning('package', "package $name extends package " .
  63029. $this->_packagexml->getExtends() . ' and so the name should ' .
  63030. 'have a postfix equal to the major version like "' .
  63031. $this->_packagexml->getExtends() . $test . '"');
  63032. return true;
  63033. } elseif (substr($name, 0, strlen($name) - $vlen) !=
  63034. $this->_packagexml->getExtends()) {
  63035. $this->_addWarning('package', "package $name extends package " .
  63036. $this->_packagexml->getExtends() . ' and so the name must ' .
  63037. 'be an extension like "' . $this->_packagexml->getExtends() .
  63038. $test . '"');
  63039. return true;
  63040. }
  63041. }
  63042. }
  63043. if (!$this->validPackageName($this->_packagexml->getPackage())) {
  63044. $this->_addFailure('name', 'package name "' .
  63045. $this->_packagexml->getPackage() . '" is invalid');
  63046. return false;
  63047. }
  63048. }
  63049. /**
  63050. * @access protected
  63051. */
  63052. function validateVersion()
  63053. {
  63054. if ($this->_state != PEAR_VALIDATE_PACKAGING) {
  63055. if (!$this->validVersion($this->_packagexml->getVersion())) {
  63056. $this->_addFailure('version',
  63057. 'Invalid version number "' . $this->_packagexml->getVersion() . '"');
  63058. }
  63059. return false;
  63060. }
  63061. $version = $this->_packagexml->getVersion();
  63062. $versioncomponents = explode('.', $version);
  63063. if (count($versioncomponents) != 3) {
  63064. $this->_addWarning('version',
  63065. 'A version number should have 3 decimals (x.y.z)');
  63066. return true;
  63067. }
  63068. $name = $this->_packagexml->getPackage();
  63069. // version must be based upon state
  63070. switch ($this->_packagexml->getState()) {
  63071. case 'snapshot' :
  63072. return true;
  63073. case 'devel' :
  63074. if ($versioncomponents[0] . 'a' == '0a') {
  63075. return true;
  63076. }
  63077. if ($versioncomponents[0] == 0) {
  63078. $versioncomponents[0] = '0';
  63079. $this->_addWarning('version',
  63080. 'version "' . $version . '" should be "' .
  63081. implode('.' ,$versioncomponents) . '"');
  63082. } else {
  63083. $this->_addWarning('version',
  63084. 'packages with devel stability must be < version 1.0.0');
  63085. }
  63086. return true;
  63087. break;
  63088. case 'alpha' :
  63089. case 'beta' :
  63090. // check for a package that extends a package,
  63091. // like Foo and Foo2
  63092. if ($this->_state == PEAR_VALIDATE_PACKAGING) {
  63093. if (substr($versioncomponents[2], 1, 2) == 'rc') {
  63094. $this->_addFailure('version', 'Release Candidate versions ' .
  63095. 'must have capital RC, not lower-case rc');
  63096. return false;
  63097. }
  63098. }
  63099. if (!$this->_packagexml->getExtends()) {
  63100. if ($versioncomponents[0] == '1') {
  63101. if ($versioncomponents[2][0] == '0') {
  63102. if ($versioncomponents[2] == '0') {
  63103. // version 1.*.0000
  63104. $this->_addWarning('version',
  63105. 'version 1.' . $versioncomponents[1] .
  63106. '.0 probably should not be alpha or beta');
  63107. return true;
  63108. } elseif (strlen($versioncomponents[2]) > 1) {
  63109. // version 1.*.0RC1 or 1.*.0beta24 etc.
  63110. return true;
  63111. } else {
  63112. // version 1.*.0
  63113. $this->_addWarning('version',
  63114. 'version 1.' . $versioncomponents[1] .
  63115. '.0 probably should not be alpha or beta');
  63116. return true;
  63117. }
  63118. } else {
  63119. $this->_addWarning('version',
  63120. 'bugfix versions (1.3.x where x > 0) probably should ' .
  63121. 'not be alpha or beta');
  63122. return true;
  63123. }
  63124. } elseif ($versioncomponents[0] != '0') {
  63125. $this->_addWarning('version',
  63126. 'major versions greater than 1 are not allowed for packages ' .
  63127. 'without an <extends> tag or an identical postfix (foo2 v2.0.0)');
  63128. return true;
  63129. }
  63130. if ($versioncomponents[0] . 'a' == '0a') {
  63131. return true;
  63132. }
  63133. if ($versioncomponents[0] == 0) {
  63134. $versioncomponents[0] = '0';
  63135. $this->_addWarning('version',
  63136. 'version "' . $version . '" should be "' .
  63137. implode('.' ,$versioncomponents) . '"');
  63138. }
  63139. } else {
  63140. $vlen = strlen($versioncomponents[0] . '');
  63141. $majver = substr($name, strlen($name) - $vlen);
  63142. while ($majver && !is_numeric($majver[0])) {
  63143. $majver = substr($majver, 1);
  63144. }
  63145. if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) {
  63146. $this->_addWarning('version', 'first version number "' .
  63147. $versioncomponents[0] . '" must match the postfix of ' .
  63148. 'package name "' . $name . '" (' .
  63149. $majver . ')');
  63150. return true;
  63151. }
  63152. if ($versioncomponents[0] == $majver) {
  63153. if ($versioncomponents[2][0] == '0') {
  63154. if ($versioncomponents[2] == '0') {
  63155. // version 2.*.0000
  63156. $this->_addWarning('version',
  63157. "version $majver." . $versioncomponents[1] .
  63158. '.0 probably should not be alpha or beta');
  63159. return false;
  63160. } elseif (strlen($versioncomponents[2]) > 1) {
  63161. // version 2.*.0RC1 or 2.*.0beta24 etc.
  63162. return true;
  63163. } else {
  63164. // version 2.*.0
  63165. $this->_addWarning('version',
  63166. "version $majver." . $versioncomponents[1] .
  63167. '.0 cannot be alpha or beta');
  63168. return true;
  63169. }
  63170. } else {
  63171. $this->_addWarning('version',
  63172. "bugfix versions ($majver.x.y where y > 0) should " .
  63173. 'not be alpha or beta');
  63174. return true;
  63175. }
  63176. } elseif ($versioncomponents[0] != '0') {
  63177. $this->_addWarning('version',
  63178. "only versions 0.x.y and $majver.x.y are allowed for alpha/beta releases");
  63179. return true;
  63180. }
  63181. if ($versioncomponents[0] . 'a' == '0a') {
  63182. return true;
  63183. }
  63184. if ($versioncomponents[0] == 0) {
  63185. $versioncomponents[0] = '0';
  63186. $this->_addWarning('version',
  63187. 'version "' . $version . '" should be "' .
  63188. implode('.' ,$versioncomponents) . '"');
  63189. }
  63190. }
  63191. return true;
  63192. break;
  63193. case 'stable' :
  63194. if ($versioncomponents[0] == '0') {
  63195. $this->_addWarning('version', 'versions less than 1.0.0 cannot ' .
  63196. 'be stable');
  63197. return true;
  63198. }
  63199. if (!is_numeric($versioncomponents[2])) {
  63200. if (preg_match('/\d+(rc|a|alpha|b|beta)\d*/i',
  63201. $versioncomponents[2])) {
  63202. $this->_addWarning('version', 'version "' . $version . '" or any ' .
  63203. 'RC/beta/alpha version cannot be stable');
  63204. return true;
  63205. }
  63206. }
  63207. // check for a package that extends a package,
  63208. // like Foo and Foo2
  63209. if ($this->_packagexml->getExtends()) {
  63210. $vlen = strlen($versioncomponents[0] . '');
  63211. $majver = substr($name, strlen($name) - $vlen);
  63212. while ($majver && !is_numeric($majver[0])) {
  63213. $majver = substr($majver, 1);
  63214. }
  63215. if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) {
  63216. $this->_addWarning('version', 'first version number "' .
  63217. $versioncomponents[0] . '" must match the postfix of ' .
  63218. 'package name "' . $name . '" (' .
  63219. $majver . ')');
  63220. return true;
  63221. }
  63222. } elseif ($versioncomponents[0] > 1) {
  63223. $this->_addWarning('version', 'major version x in x.y.z may not be greater than ' .
  63224. '1 for any package that does not have an <extends> tag');
  63225. }
  63226. return true;
  63227. break;
  63228. default :
  63229. return false;
  63230. break;
  63231. }
  63232. }
  63233. /**
  63234. * @access protected
  63235. */
  63236. function validateMaintainers()
  63237. {
  63238. // maintainers can only be truly validated server-side for most channels
  63239. // but allow this customization for those who wish it
  63240. return true;
  63241. }
  63242. /**
  63243. * @access protected
  63244. */
  63245. function validateDate()
  63246. {
  63247. if ($this->_state == PEAR_VALIDATE_NORMAL ||
  63248. $this->_state == PEAR_VALIDATE_PACKAGING) {
  63249. if (!preg_match('/(\d\d\d\d)\-(\d\d)\-(\d\d)/',
  63250. $this->_packagexml->getDate(), $res) ||
  63251. count($res) < 4
  63252. || !checkdate($res[2], $res[3], $res[1])
  63253. ) {
  63254. $this->_addFailure('date', 'invalid release date "' .
  63255. $this->_packagexml->getDate() . '"');
  63256. return false;
  63257. }
  63258. if ($this->_state == PEAR_VALIDATE_PACKAGING &&
  63259. $this->_packagexml->getDate() != date('Y-m-d')) {
  63260. $this->_addWarning('date', 'Release Date "' .
  63261. $this->_packagexml->getDate() . '" is not today');
  63262. }
  63263. }
  63264. return true;
  63265. }
  63266. /**
  63267. * @access protected
  63268. */
  63269. function validateTime()
  63270. {
  63271. if (!$this->_packagexml->getTime()) {
  63272. // default of no time value set
  63273. return true;
  63274. }
  63275. // packager automatically sets time, so only validate if pear validate is called
  63276. if ($this->_state = PEAR_VALIDATE_NORMAL) {
  63277. if (!preg_match('/\d\d:\d\d:\d\d/',
  63278. $this->_packagexml->getTime())) {
  63279. $this->_addFailure('time', 'invalid release time "' .
  63280. $this->_packagexml->getTime() . '"');
  63281. return false;
  63282. }
  63283. $result = preg_match('|\d{2}\:\d{2}\:\d{2}|', $this->_packagexml->getTime(), $matches);
  63284. if ($result === false || empty($matches)) {
  63285. $this->_addFailure('time', 'invalid release time "' .
  63286. $this->_packagexml->getTime() . '"');
  63287. return false;
  63288. }
  63289. }
  63290. return true;
  63291. }
  63292. /**
  63293. * @access protected
  63294. */
  63295. function validateState()
  63296. {
  63297. // this is the closest to "final" php4 can get
  63298. if (!PEAR_Validate::validState($this->_packagexml->getState())) {
  63299. if (strtolower($this->_packagexml->getState() == 'rc')) {
  63300. $this->_addFailure('state', 'RC is not a state, it is a version ' .
  63301. 'postfix, use ' . $this->_packagexml->getVersion() . 'RC1, state beta');
  63302. }
  63303. $this->_addFailure('state', 'invalid release state "' .
  63304. $this->_packagexml->getState() . '", must be one of: ' .
  63305. implode(', ', PEAR_Validate::getValidStates()));
  63306. return false;
  63307. }
  63308. return true;
  63309. }
  63310. /**
  63311. * @access protected
  63312. */
  63313. function validateStability()
  63314. {
  63315. $ret = true;
  63316. $packagestability = $this->_packagexml->getState();
  63317. $apistability = $this->_packagexml->getState('api');
  63318. if (!PEAR_Validate::validState($packagestability)) {
  63319. $this->_addFailure('state', 'invalid release stability "' .
  63320. $this->_packagexml->getState() . '", must be one of: ' .
  63321. implode(', ', PEAR_Validate::getValidStates()));
  63322. $ret = false;
  63323. }
  63324. $apistates = PEAR_Validate::getValidStates();
  63325. array_shift($apistates); // snapshot is not allowed
  63326. if (!in_array($apistability, $apistates)) {
  63327. $this->_addFailure('state', 'invalid API stability "' .
  63328. $this->_packagexml->getState('api') . '", must be one of: ' .
  63329. implode(', ', $apistates));
  63330. $ret = false;
  63331. }
  63332. return $ret;
  63333. }
  63334. /**
  63335. * @access protected
  63336. */
  63337. function validateSummary()
  63338. {
  63339. return true;
  63340. }
  63341. /**
  63342. * @access protected
  63343. */
  63344. function validateDescription()
  63345. {
  63346. return true;
  63347. }
  63348. /**
  63349. * @access protected
  63350. */
  63351. function validateLicense()
  63352. {
  63353. return true;
  63354. }
  63355. /**
  63356. * @access protected
  63357. */
  63358. function validateNotes()
  63359. {
  63360. return true;
  63361. }
  63362. /**
  63363. * for package.xml 2.0 only - channels can't use package.xml 1.0
  63364. * @access protected
  63365. */
  63366. function validateDependencies()
  63367. {
  63368. return true;
  63369. }
  63370. /**
  63371. * for package.xml 1.0 only
  63372. * @access private
  63373. */
  63374. function _validateFilelist()
  63375. {
  63376. return true; // placeholder for now
  63377. }
  63378. /**
  63379. * for package.xml 2.0 only
  63380. * @access protected
  63381. */
  63382. function validateMainFilelist()
  63383. {
  63384. return true; // placeholder for now
  63385. }
  63386. /**
  63387. * for package.xml 2.0 only
  63388. * @access protected
  63389. */
  63390. function validateReleaseFilelist()
  63391. {
  63392. return true; // placeholder for now
  63393. }
  63394. /**
  63395. * @access protected
  63396. */
  63397. function validateChangelog()
  63398. {
  63399. return true;
  63400. }
  63401. /**
  63402. * @access protected
  63403. */
  63404. function validateFilelist()
  63405. {
  63406. return true;
  63407. }
  63408. /**
  63409. * @access protected
  63410. */
  63411. function validateDeps()
  63412. {
  63413. return true;
  63414. }
  63415. }�����������PEAR-1.10.16/PEAR/XMLParser.php���������������������������������������������������������������������0000644�0001750�0001750�00000015377�14720722517�015360� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  63416. /**
  63417. * PEAR_XMLParser
  63418. *
  63419. * PHP versions 4 and 5
  63420. *
  63421. * @category pear
  63422. * @package PEAR
  63423. * @author Greg Beaver <cellog@php.net>
  63424. * @author Stephan Schmidt (original XML_Unserializer code)
  63425. * @copyright 1997-2009 The Authors
  63426. * @license http://opensource.org/licenses/bsd-license New BSD License
  63427. * @link http://pear.php.net/package/PEAR
  63428. * @since File available since Release 1.4.0a1
  63429. */
  63430. /**
  63431. * Parser for any xml file
  63432. * @category pear
  63433. * @package PEAR
  63434. * @author Greg Beaver <cellog@php.net>
  63435. * @author Stephan Schmidt (original XML_Unserializer code)
  63436. * @copyright 1997-2009 The Authors
  63437. * @license http://opensource.org/licenses/bsd-license New BSD License
  63438. * @version Release: 1.10.16
  63439. * @link http://pear.php.net/package/PEAR
  63440. * @since Class available since Release 1.4.0a1
  63441. */
  63442. class PEAR_XMLParser
  63443. {
  63444. /**
  63445. * unserilialized data
  63446. * @var string $_serializedData
  63447. */
  63448. var $_unserializedData = null;
  63449. /**
  63450. * name of the root tag
  63451. * @var string $_root
  63452. */
  63453. var $_root = null;
  63454. /**
  63455. * stack for all data that is found
  63456. * @var array $_dataStack
  63457. */
  63458. var $_dataStack = array();
  63459. /**
  63460. * stack for all values that are generated
  63461. * @var array $_valStack
  63462. */
  63463. var $_valStack = array();
  63464. /**
  63465. * current tag depth
  63466. * @var int $_depth
  63467. */
  63468. var $_depth = 0;
  63469. /**
  63470. * The XML encoding to use
  63471. * @var string $encoding
  63472. */
  63473. var $encoding = 'ISO-8859-1';
  63474. /**
  63475. * @return array
  63476. */
  63477. function getData()
  63478. {
  63479. return $this->_unserializedData;
  63480. }
  63481. /**
  63482. * @param string xml content
  63483. * @return true|PEAR_Error
  63484. */
  63485. function parse($data)
  63486. {
  63487. if (!extension_loaded('xml')) {
  63488. include_once 'PEAR.php';
  63489. return PEAR::raiseError("XML Extension not found", 1);
  63490. }
  63491. $this->_dataStack = $this->_valStack = array();
  63492. $this->_depth = 0;
  63493. if (
  63494. strpos($data, 'encoding="UTF-8"')
  63495. || strpos($data, 'encoding="utf-8"')
  63496. || strpos($data, "encoding='UTF-8'")
  63497. || strpos($data, "encoding='utf-8'")
  63498. ) {
  63499. $this->encoding = 'UTF-8';
  63500. }
  63501. $xp = xml_parser_create($this->encoding);
  63502. xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, 0);
  63503. xml_set_object($xp, $this);
  63504. xml_set_element_handler($xp, 'startHandler', 'endHandler');
  63505. xml_set_character_data_handler($xp, 'cdataHandler');
  63506. if (!xml_parse($xp, $data)) {
  63507. $msg = xml_error_string(xml_get_error_code($xp));
  63508. $line = xml_get_current_line_number($xp);
  63509. xml_parser_free($xp);
  63510. include_once 'PEAR.php';
  63511. return PEAR::raiseError("XML Error: '$msg' on line '$line'", 2);
  63512. }
  63513. xml_parser_free($xp);
  63514. return true;
  63515. }
  63516. /**
  63517. * Start element handler for XML parser
  63518. *
  63519. * @access private
  63520. * @param object $parser XML parser object
  63521. * @param string $element XML element
  63522. * @param array $attribs attributes of XML tag
  63523. * @return void
  63524. */
  63525. function startHandler($parser, $element, $attribs)
  63526. {
  63527. $this->_depth++;
  63528. $this->_dataStack[$this->_depth] = null;
  63529. $val = array(
  63530. 'name' => $element,
  63531. 'value' => null,
  63532. 'type' => 'string',
  63533. 'childrenKeys' => array(),
  63534. 'aggregKeys' => array()
  63535. );
  63536. if (count($attribs) > 0) {
  63537. $val['children'] = array();
  63538. $val['type'] = 'array';
  63539. $val['children']['attribs'] = $attribs;
  63540. }
  63541. array_push($this->_valStack, $val);
  63542. }
  63543. /**
  63544. * post-process data
  63545. *
  63546. * @param string $data
  63547. * @param string $element element name
  63548. */
  63549. function postProcess($data, $element)
  63550. {
  63551. return trim($data);
  63552. }
  63553. /**
  63554. * End element handler for XML parser
  63555. *
  63556. * @access private
  63557. * @param object XML parser object
  63558. * @param string
  63559. * @return void
  63560. */
  63561. function endHandler($parser, $element)
  63562. {
  63563. $value = array_pop($this->_valStack);
  63564. $data = $this->postProcess($this->_dataStack[$this->_depth], $element);
  63565. // adjust type of the value
  63566. switch (strtolower($value['type'])) {
  63567. // unserialize an array
  63568. case 'array':
  63569. if ($data !== '') {
  63570. $value['children']['_content'] = $data;
  63571. }
  63572. $value['value'] = isset($value['children']) ? $value['children'] : array();
  63573. break;
  63574. /*
  63575. * unserialize a null value
  63576. */
  63577. case 'null':
  63578. $data = null;
  63579. break;
  63580. /*
  63581. * unserialize any scalar value
  63582. */
  63583. default:
  63584. settype($data, $value['type']);
  63585. $value['value'] = $data;
  63586. break;
  63587. }
  63588. $parent = array_pop($this->_valStack);
  63589. if ($parent === null) {
  63590. $this->_unserializedData = &$value['value'];
  63591. $this->_root = &$value['name'];
  63592. return true;
  63593. }
  63594. // parent has to be an array
  63595. if (!isset($parent['children']) || !is_array($parent['children'])) {
  63596. $parent['children'] = array();
  63597. if ($parent['type'] != 'array') {
  63598. $parent['type'] = 'array';
  63599. }
  63600. }
  63601. if (!empty($value['name'])) {
  63602. // there already has been a tag with this name
  63603. if (in_array($value['name'], $parent['childrenKeys'])) {
  63604. // no aggregate has been created for this tag
  63605. if (!in_array($value['name'], $parent['aggregKeys'])) {
  63606. if (isset($parent['children'][$value['name']])) {
  63607. $parent['children'][$value['name']] = array($parent['children'][$value['name']]);
  63608. } else {
  63609. $parent['children'][$value['name']] = array();
  63610. }
  63611. array_push($parent['aggregKeys'], $value['name']);
  63612. }
  63613. array_push($parent['children'][$value['name']], $value['value']);
  63614. } else {
  63615. $parent['children'][$value['name']] = &$value['value'];
  63616. array_push($parent['childrenKeys'], $value['name']);
  63617. }
  63618. } else {
  63619. array_push($parent['children'],$value['value']);
  63620. }
  63621. array_push($this->_valStack, $parent);
  63622. $this->_depth--;
  63623. }
  63624. /**
  63625. * Handler for character data
  63626. *
  63627. * @access private
  63628. * @param object XML parser object
  63629. * @param string CDATA
  63630. * @return void
  63631. */
  63632. function cdataHandler($parser, $cdata)
  63633. {
  63634. $this->_dataStack[$this->_depth] .= $cdata;
  63635. }
  63636. }�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/scripts/pear.bat�����������������������������������������������������������������������0000755�0001750�0001750�00000010361�14720722517�015400� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@ECHO OFF
  63637. REM ----------------------------------------------------------------------
  63638. REM PHP version 5
  63639. REM ----------------------------------------------------------------------
  63640. REM Copyright (c) 1997-2010 The Authors
  63641. REM ----------------------------------------------------------------------
  63642. REM http://opensource.org/licenses/bsd-license.php New BSD License
  63643. REM ----------------------------------------------------------------------
  63644. REM Authors: Alexander Merz (alexmerz@php.net)
  63645. REM ----------------------------------------------------------------------
  63646. REM
  63647. REM Last updated 12/29/2004 ($Id$ is not replaced if the file is binary)
  63648. REM change this lines to match the paths of your system
  63649. REM -------------------
  63650. REM Test to see if this is a raw pear.bat (uninstalled version)
  63651. SET TMPTMPTMPTMPT=@includ
  63652. SET PMTPMTPMT=%TMPTMPTMPTMPT%e_path@
  63653. FOR %%x IN ("@include_path@") DO (if %%x=="%PMTPMTPMT%" GOTO :NOTINSTALLED)
  63654. REM Check PEAR global ENV, set them if they do not exist
  63655. IF "%PHP_PEAR_INSTALL_DIR%"=="" SET "PHP_PEAR_INSTALL_DIR=@include_path@"
  63656. IF "%PHP_PEAR_BIN_DIR%"=="" SET "PHP_PEAR_BIN_DIR=@bin_dir@"
  63657. IF "%PHP_PEAR_PHP_BIN%"=="" SET "PHP_PEAR_PHP_BIN=@php_bin@"
  63658. GOTO :INSTALLED
  63659. :NOTINSTALLED
  63660. ECHO WARNING: This is a raw, uninstalled pear.bat
  63661. REM Check to see if we can grab the directory of this file (Windows NT+)
  63662. IF %~n0 == pear (
  63663. FOR %%x IN (cli\php.exe php.exe) DO (if "%%~$PATH:x" NEQ "" (
  63664. SET "PHP_PEAR_PHP_BIN=%%~$PATH:x"
  63665. echo Using PHP Executable "%PHP_PEAR_PHP_BIN%"
  63666. "%PHP_PEAR_PHP_BIN%" -v
  63667. GOTO :NEXTTEST
  63668. ))
  63669. GOTO :FAILAUTODETECT
  63670. :NEXTTEST
  63671. IF "%PHP_PEAR_PHP_BIN%" NEQ "" (
  63672. REM We can use this PHP to run a temporary php file to get the dirname of pear
  63673. echo ^<?php $s=getcwd^(^);chdir^($a=dirname^(__FILE__^).'\\'^);if^(stristr^($a,'\\scripts'^)^)$a=dirname^(dirname^($a^)^).'\\';$f=fopen^($s.'\\~a.a','wb'^);echo$s.'\\~a.a';fwrite^($f,$a^);fclose^($f^);chdir^($s^);?^> > ~~getloc.php
  63674. "%PHP_PEAR_PHP_BIN%" ~~getloc.php
  63675. set /p PHP_PEAR_BIN_DIR=fakeprompt < ~a.a
  63676. DEL ~a.a
  63677. DEL ~~getloc.php
  63678. set "PHP_PEAR_INSTALL_DIR=%PHP_PEAR_BIN_DIR%pear"
  63679. REM Make sure there is a pearcmd.php at our disposal
  63680. IF NOT EXIST %PHP_PEAR_INSTALL_DIR%\pearcmd.php (
  63681. IF EXIST %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php COPY %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
  63682. IF EXIST pearcmd.php COPY pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
  63683. IF EXIST %~dp0\scripts\pearcmd.php COPY %~dp0\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
  63684. )
  63685. )
  63686. GOTO :INSTALLED
  63687. ) ELSE (
  63688. REM Windows Me/98 cannot succeed, so allow the batch to fail
  63689. )
  63690. :FAILAUTODETECT
  63691. echo WARNING: failed to auto-detect pear information
  63692. :INSTALLED
  63693. REM Check Folders and files
  63694. IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%" GOTO PEAR_INSTALL_ERROR
  63695. IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%\pearcmd.php" GOTO PEAR_INSTALL_ERROR2
  63696. IF NOT EXIST "%PHP_PEAR_BIN_DIR%" GOTO PEAR_BIN_ERROR
  63697. IF NOT EXIST "%PHP_PEAR_PHP_BIN%" GOTO PEAR_PHPBIN_ERROR
  63698. REM launch pearcmd
  63699. GOTO RUN
  63700. :PEAR_INSTALL_ERROR
  63701. ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
  63702. ECHO Please fix it using your environment variable or modify
  63703. ECHO the default value in pear.bat
  63704. ECHO The current value is:
  63705. ECHO %PHP_PEAR_INSTALL_DIR%
  63706. GOTO END
  63707. :PEAR_INSTALL_ERROR2
  63708. ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
  63709. ECHO pearcmd.php could not be found there.
  63710. ECHO Please fix it using your environment variable or modify
  63711. ECHO the default value in pear.bat
  63712. ECHO The current value is:
  63713. ECHO %PHP_PEAR_INSTALL_DIR%
  63714. GOTO END
  63715. :PEAR_BIN_ERROR
  63716. ECHO PHP_PEAR_BIN_DIR is not set correctly.
  63717. ECHO Please fix it using your environment variable or modify
  63718. ECHO the default value in pear.bat
  63719. ECHO The current value is:
  63720. ECHO %PHP_PEAR_BIN_DIR%
  63721. GOTO END
  63722. :PEAR_PHPBIN_ERROR
  63723. ECHO PHP_PEAR_PHP_BIN is not set correctly.
  63724. ECHO Please fix it using your environment variable or modify
  63725. ECHO the default value in pear.bat
  63726. ECHO The current value is:
  63727. ECHO %PHP_PEAR_PHP_BIN%
  63728. GOTO END
  63729. :RUN
  63730. "%PHP_PEAR_PHP_BIN%" -C -d date.timezone=UTC -d output_buffering=1 -d safe_mode=0 -d open_basedir="" -d auto_prepend_file="" -d auto_append_file="" -d variables_order=EGPCS -d register_argc_argv="On" -d "include_path='%PHP_PEAR_INSTALL_DIR%'" -f "%PHP_PEAR_INSTALL_DIR%\pearcmd.php" -- %1 %2 %3 %4 %5 %6 %7 %8 %9
  63731. :END
  63732. @ECHO ON�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/scripts/peardev.bat��������������������������������������������������������������������0000664�0001750�0001750�00000011050�14720722517�016072� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@ECHO OFF
  63733. REM ----------------------------------------------------------------------
  63734. REM PHP version 5
  63735. REM ----------------------------------------------------------------------
  63736. REM Copyright (c) 1997-2004 The PHP Group
  63737. REM ----------------------------------------------------------------------
  63738. REM This source file is subject to the New BSD License,
  63739. REM that is bundled with this package in the file LICENSE, and is
  63740. REM available at through the world-wide-web at
  63741. REM http://opensource.org/licenses/bsd-license.php.
  63742. REM If you did not receive a copy of the New BSD License and are unable to
  63743. REM obtain it through the world-wide-web, please send a note to
  63744. REM license@php.net so we can mail you a copy immediately.
  63745. REM ----------------------------------------------------------------------
  63746. REM Authors: Alexander Merz (alexmerz@php.net)
  63747. REM ----------------------------------------------------------------------
  63748. REM
  63749. REM change this lines to match the paths of your system
  63750. REM -------------------
  63751. REM Test to see if this is a raw pear.bat (uninstalled version)
  63752. SET TMPTMPTMPTMPT=@includ
  63753. SET PMTPMTPMT=%TMPTMPTMPTMPT%e_path@
  63754. FOR %%x IN ("@include_path@") DO (if %%x=="%PMTPMTPMT%" GOTO :NOTINSTALLED)
  63755. REM Check PEAR global ENV, set them if they do not exist
  63756. IF "%PHP_PEAR_INSTALL_DIR%"=="" SET "PHP_PEAR_INSTALL_DIR=@include_path@"
  63757. IF "%PHP_PEAR_BIN_DIR%"=="" SET "PHP_PEAR_BIN_DIR=@bin_dir@"
  63758. IF "%PHP_PEAR_PHP_BIN%"=="" SET "PHP_PEAR_PHP_BIN=@php_bin@"
  63759. GOTO :INSTALLED
  63760. :NOTINSTALLED
  63761. ECHO WARNING: This is a raw, uninstalled pear.bat
  63762. REM Check to see if we can grab the directory of this file (Windows NT+)
  63763. IF %~n0 == pear (
  63764. FOR %%x IN (cli\php.exe php.exe) DO (if "%%~$PATH:x" NEQ "" (
  63765. SET "PHP_PEAR_PHP_BIN=%%~$PATH:x"
  63766. echo Using PHP Executable "%PHP_PEAR_PHP_BIN%"
  63767. "%PHP_PEAR_PHP_BIN%" -v
  63768. GOTO :NEXTTEST
  63769. ))
  63770. GOTO :FAILAUTODETECT
  63771. :NEXTTEST
  63772. IF "%PHP_PEAR_PHP_BIN%" NEQ "" (
  63773. REM We can use this PHP to run a temporary php file to get the dirname of pear
  63774. echo ^<?php $s=getcwd^(^);chdir^($a=dirname^(__FILE__^).'\\'^);if^(stristr^($a,'\\scripts'^)^)$a=dirname^(dirname^($a^)^).'\\';$f=fopen^($s.'\\~a.a','wb'^);echo$s.'\\~a.a';fwrite^($f,$a^);fclose^($f^);chdir^($s^);?^> > ~~getloc.php
  63775. "%PHP_PEAR_PHP_BIN%" ~~getloc.php
  63776. set /p PHP_PEAR_BIN_DIR=fakeprompt < ~a.a
  63777. DEL ~a.a
  63778. DEL ~~getloc.php
  63779. set "PHP_PEAR_INSTALL_DIR=%PHP_PEAR_BIN_DIR%pear"
  63780. REM Make sure there is a pearcmd.php at our disposal
  63781. IF NOT EXIST %PHP_PEAR_INSTALL_DIR%\pearcmd.php (
  63782. IF EXIST %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php COPY %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
  63783. IF EXIST pearcmd.php COPY pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
  63784. IF EXIST %~dp0\scripts\pearcmd.php COPY %~dp0\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
  63785. )
  63786. )
  63787. GOTO :INSTALLED
  63788. ) ELSE (
  63789. REM Windows Me/98 cannot succeed, so allow the batch to fail
  63790. )
  63791. :FAILAUTODETECT
  63792. echo WARNING: failed to auto-detect pear information
  63793. :INSTALLED
  63794. REM Check Folders and files
  63795. IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%" GOTO PEAR_INSTALL_ERROR
  63796. IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%\pearcmd.php" GOTO PEAR_INSTALL_ERROR2
  63797. IF NOT EXIST "%PHP_PEAR_BIN_DIR%" GOTO PEAR_BIN_ERROR
  63798. IF NOT EXIST "%PHP_PEAR_PHP_BIN%" GOTO PEAR_PHPBIN_ERROR
  63799. REM launch pearcmd
  63800. GOTO RUN
  63801. :PEAR_INSTALL_ERROR
  63802. ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
  63803. ECHO Please fix it using your environment variable or modify
  63804. ECHO the default value in pear.bat
  63805. ECHO The current value is:
  63806. ECHO %PHP_PEAR_INSTALL_DIR%
  63807. GOTO END
  63808. :PEAR_INSTALL_ERROR2
  63809. ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
  63810. ECHO pearcmd.php could not be found there.
  63811. ECHO Please fix it using your environment variable or modify
  63812. ECHO the default value in pear.bat
  63813. ECHO The current value is:
  63814. ECHO %PHP_PEAR_INSTALL_DIR%
  63815. GOTO END
  63816. :PEAR_BIN_ERROR
  63817. ECHO PHP_PEAR_BIN_DIR is not set correctly.
  63818. ECHO Please fix it using your environment variable or modify
  63819. ECHO the default value in pear.bat
  63820. ECHO The current value is:
  63821. ECHO %PHP_PEAR_BIN_DIR%
  63822. GOTO END
  63823. :PEAR_PHPBIN_ERROR
  63824. ECHO PHP_PEAR_PHP_BIN is not set correctly.
  63825. ECHO Please fix it using your environment variable or modify
  63826. ECHO the default value in pear.bat
  63827. ECHO The current value is:
  63828. ECHO %PHP_PEAR_PHP_BIN%
  63829. GOTO END
  63830. :RUN
  63831. "%PHP_PEAR_PHP_BIN%" -C -d date.timezone=UTC -d memory_limit="-1" -d safe_mode=0 -d register_argc_argv="On" -d auto_prepend_file="" -d auto_append_file="" -d variables_order=EGPCS -d open_basedir="" -d output_buffering=1 -d "include_path='%PHP_PEAR_INSTALL_DIR%'" -f "%PHP_PEAR_INSTALL_DIR%\pearcmd.php" -- %1 %2 %3 %4 %5 %6 %7 %8 %9
  63832. :END
  63833. @ECHO ON
  63834. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/scripts/pecl.bat�����������������������������������������������������������������������0000664�0001750�0001750�00000010721�14720722517�015373� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@ECHO OFF
  63835. REM ----------------------------------------------------------------------
  63836. REM PHP version 5
  63837. REM ----------------------------------------------------------------------
  63838. REM Copyright (c) 1997-2004 The PHP Group
  63839. REM ----------------------------------------------------------------------
  63840. REM This source file is subject to the New BSD License,
  63841. REM that is bundled with this package in the file LICENSE, and is
  63842. REM available at through the world-wide-web at
  63843. REM http://opensource.org/licenses/bsd-license.php.
  63844. REM If you did not receive a copy of the New BSD License and are unable to
  63845. REM obtain it through the world-wide-web, please send a note to
  63846. REM license@php.net so we can mail you a copy immediately.
  63847. REM ----------------------------------------------------------------------
  63848. REM Authors: Alexander Merz (alexmerz@php.net)
  63849. REM ----------------------------------------------------------------------
  63850. REM
  63851. REM change this lines to match the paths of your system
  63852. REM -------------------
  63853. REM Test to see if this is a raw pear.bat (uninstalled version)
  63854. SET TMPTMPTMPTMPT=@includ
  63855. SET PMTPMTPMT=%TMPTMPTMPTMPT%e_path@
  63856. FOR %%x IN ("@include_path@") DO (if %%x=="%PMTPMTPMT%" GOTO :NOTINSTALLED)
  63857. REM Check PEAR global ENV, set them if they do not exist
  63858. IF "%PHP_PEAR_INSTALL_DIR%"=="" SET "PHP_PEAR_INSTALL_DIR=@include_path@"
  63859. IF "%PHP_PEAR_BIN_DIR%"=="" SET "PHP_PEAR_BIN_DIR=@bin_dir@"
  63860. IF "%PHP_PEAR_PHP_BIN%"=="" SET "PHP_PEAR_PHP_BIN=@php_bin@"
  63861. GOTO :INSTALLED
  63862. :NOTINSTALLED
  63863. ECHO WARNING: This is a raw, uninstalled pear.bat
  63864. REM Check to see if we can grab the directory of this file (Windows NT+)
  63865. IF %~n0 == pear (
  63866. FOR %%x IN (cli\php.exe php.exe) DO (if "%%~$PATH:x" NEQ "" (
  63867. SET "PHP_PEAR_PHP_BIN=%%~$PATH:x"
  63868. echo Using PHP Executable "%PHP_PEAR_PHP_BIN%"
  63869. "%PHP_PEAR_PHP_BIN%" -v
  63870. GOTO :NEXTTEST
  63871. ))
  63872. GOTO :FAILAUTODETECT
  63873. :NEXTTEST
  63874. IF "%PHP_PEAR_PHP_BIN%" NEQ "" (
  63875. REM We can use this PHP to run a temporary php file to get the dirname of pear
  63876. echo ^<?php $s=getcwd^(^);chdir^($a=dirname^(__FILE__^).'\\'^);if^(stristr^($a,'\\scripts'^)^)$a=dirname^(dirname^($a^)^).'\\';$f=fopen^($s.'\\~a.a','wb'^);echo$s.'\\~a.a';fwrite^($f,$a^);fclose^($f^);chdir^($s^);?^> > ~~getloc.php
  63877. "%PHP_PEAR_PHP_BIN%" ~~getloc.php
  63878. set /p PHP_PEAR_BIN_DIR=fakeprompt < ~a.a
  63879. DEL ~a.a
  63880. DEL ~~getloc.php
  63881. set "PHP_PEAR_INSTALL_DIR=%PHP_PEAR_BIN_DIR%pear"
  63882. REM Make sure there is a pearcmd.php at our disposal
  63883. IF NOT EXIST %PHP_PEAR_INSTALL_DIR%\pearcmd.php (
  63884. IF EXIST %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php COPY %PHP_PEAR_INSTALL_DIR%\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
  63885. IF EXIST pearcmd.php COPY pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
  63886. IF EXIST %~dp0\scripts\pearcmd.php COPY %~dp0\scripts\pearcmd.php %PHP_PEAR_INSTALL_DIR%\pearcmd.php
  63887. )
  63888. )
  63889. GOTO :INSTALLED
  63890. ) ELSE (
  63891. REM Windows Me/98 cannot succeed, so allow the batch to fail
  63892. )
  63893. :FAILAUTODETECT
  63894. echo WARNING: failed to auto-detect pear information
  63895. :INSTALLED
  63896. REM Check Folders and files
  63897. IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%" GOTO PEAR_INSTALL_ERROR
  63898. IF NOT EXIST "%PHP_PEAR_INSTALL_DIR%\pearcmd.php" GOTO PEAR_INSTALL_ERROR2
  63899. IF NOT EXIST "%PHP_PEAR_BIN_DIR%" GOTO PEAR_BIN_ERROR
  63900. IF NOT EXIST "%PHP_PEAR_PHP_BIN%" GOTO PEAR_PHPBIN_ERROR
  63901. REM launch pearcmd
  63902. GOTO RUN
  63903. :PEAR_INSTALL_ERROR
  63904. ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
  63905. ECHO Please fix it using your environment variable or modify
  63906. ECHO the default value in pear.bat
  63907. ECHO The current value is:
  63908. ECHO %PHP_PEAR_INSTALL_DIR%
  63909. GOTO END
  63910. :PEAR_INSTALL_ERROR2
  63911. ECHO PHP_PEAR_INSTALL_DIR is not set correctly.
  63912. ECHO pearcmd.php could not be found there.
  63913. ECHO Please fix it using your environment variable or modify
  63914. ECHO the default value in pear.bat
  63915. ECHO The current value is:
  63916. ECHO %PHP_PEAR_INSTALL_DIR%
  63917. GOTO END
  63918. :PEAR_BIN_ERROR
  63919. ECHO PHP_PEAR_BIN_DIR is not set correctly.
  63920. ECHO Please fix it using your environment variable or modify
  63921. ECHO the default value in pear.bat
  63922. ECHO The current value is:
  63923. ECHO %PHP_PEAR_BIN_DIR%
  63924. GOTO END
  63925. :PEAR_PHPBIN_ERROR
  63926. ECHO PHP_PEAR_PHP_BIN is not set correctly.
  63927. ECHO Please fix it using your environment variable or modify
  63928. ECHO the default value in pear.bat
  63929. ECHO The current value is:
  63930. ECHO %PHP_PEAR_PHP_BIN%
  63931. GOTO END
  63932. :RUN
  63933. "%PHP_PEAR_PHP_BIN%" -C -d date.timezone=UTC -d output_buffering=1 -d safe_mode=0 -d "include_path='%PHP_PEAR_INSTALL_DIR%'" -d register_argc_argv="On" -d variables_order=EGPCS -f "%PHP_PEAR_INSTALL_DIR%\peclcmd.php" -- %1 %2 %3 %4 %5 %6 %7 %8 %9
  63934. :END
  63935. @ECHO ON
  63936. �����������������������������������������������PEAR-1.10.16/scripts/pear.sh������������������������������������������������������������������������0000775�0001750�0001750�00000001404�14720722517�015244� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh
  63937. # first find which PHP binary to use
  63938. if test "x$PHP_PEAR_PHP_BIN" != "x"; then
  63939. PHP="$PHP_PEAR_PHP_BIN"
  63940. else
  63941. if test "@php_bin@" = '@'php_bin'@'; then
  63942. PHP=php
  63943. else
  63944. PHP="@php_bin@"
  63945. fi
  63946. fi
  63947. # then look for the right pear include dir
  63948. if test "x$PHP_PEAR_INSTALL_DIR" != "x"; then
  63949. INCDIR=$PHP_PEAR_INSTALL_DIR
  63950. INCARG="-d include_path=$PHP_PEAR_INSTALL_DIR"
  63951. else
  63952. if test "@php_dir@" = '@'php_dir'@'; then
  63953. INCDIR=`dirname $0`
  63954. INCARG=""
  63955. else
  63956. INCDIR="@php_dir@"
  63957. INCARG="-d include_path=@php_dir@"
  63958. fi
  63959. fi
  63960. exec $PHP -C -q $INCARG -d date.timezone=UTC -d output_buffering=1 -d variables_order=EGPCS -d open_basedir="" -d safe_mode=0 -d register_argc_argv="On" -d auto_prepend_file="" -d auto_append_file="" $INCDIR/pearcmd.php "$@"
  63961. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/scripts/peardev.sh���������������������������������������������������������������������0000755�0001750�0001750�00000001431�14720722517�015741� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh
  63962. # first find which PHP binary to use
  63963. if test "x$PHP_PEAR_PHP_BIN" != "x"; then
  63964. PHP="$PHP_PEAR_PHP_BIN"
  63965. else
  63966. if test "@php_bin@" = '@'php_bin'@'; then
  63967. PHP=php
  63968. else
  63969. PHP="@php_bin@"
  63970. fi
  63971. fi
  63972. # then look for the right pear include dir
  63973. if test "x$PHP_PEAR_INSTALL_DIR" != "x"; then
  63974. INCDIR=$PHP_PEAR_INSTALL_DIR
  63975. INCARG="-d include_path=$PHP_PEAR_INSTALL_DIR"
  63976. else
  63977. if test "@php_dir@" = '@'php_dir'@'; then
  63978. INCDIR=`dirname $0`
  63979. INCARG=""
  63980. else
  63981. INCDIR="@php_dir@"
  63982. INCARG="-d include_path=@php_dir@"
  63983. fi
  63984. fi
  63985. exec $PHP -d date.timezone=UTC -d memory_limit="-1" -C -q $INCARG -d output_buffering=1 -d open_basedir="" -d safe_mode=0 -d register_argc_argv="On" -d auto_prepend_file="" -d variables_order=EGPCS -d auto_append_file="" $INCDIR/pearcmd.php "$@"
  63986. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/scripts/pecl.sh������������������������������������������������������������������������0000755�0001750�0001750�00000001302�14720722517�015233� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh
  63987. # first find which PHP binary to use
  63988. if test "x$PHP_PEAR_PHP_BIN" != "x"; then
  63989. PHP="$PHP_PEAR_PHP_BIN"
  63990. else
  63991. if test "@php_bin@" = '@'php_bin'@'; then
  63992. PHP=php
  63993. else
  63994. PHP="@php_bin@"
  63995. fi
  63996. fi
  63997. # then look for the right pear include dir
  63998. if test "x$PHP_PEAR_INSTALL_DIR" != "x"; then
  63999. INCDIR=$PHP_PEAR_INSTALL_DIR
  64000. INCARG="-d include_path=$PHP_PEAR_INSTALL_DIR"
  64001. else
  64002. if test "@php_dir@" = '@'php_dir'@'; then
  64003. INCDIR=`dirname $0`
  64004. INCARG=""
  64005. else
  64006. INCDIR="@php_dir@"
  64007. INCARG="-d include_path=@php_dir@"
  64008. fi
  64009. fi
  64010. exec $PHP -C -q $INCARG -d date.timezone=UTC -d output_buffering=1 -d variables_order=EGPCS -d safe_mode=0 -d register_argc_argv="On" $INCDIR/peclcmd.php "$@"
  64011. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/scripts/pearcmd.php��������������������������������������������������������������������0000664�0001750�0001750�00000035174�14720722517�016115� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  64012. /**
  64013. * PEAR, the PHP Extension and Application Repository
  64014. *
  64015. * Command line interface
  64016. *
  64017. * PHP versions 4 and 5
  64018. *
  64019. * @category pear
  64020. * @package PEAR
  64021. * @author Stig Bakken <ssb@php.net>
  64022. * @author Tomas V.V.Cox <cox@idecnet.com>
  64023. * @copyright 1997-2009 The Authors
  64024. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  64025. * @link http://pear.php.net/package/PEAR
  64026. */
  64027. @ob_end_clean();
  64028. if (!defined('PEAR_RUNTYPE')) {
  64029. // this is defined in peclcmd.php as 'pecl'
  64030. define('PEAR_RUNTYPE', 'pear');
  64031. }
  64032. define('PEAR_IGNORE_BACKTRACE', 1);
  64033. /**
  64034. * @nodep Gtk
  64035. */
  64036. //the space is needed for windows include paths with trailing backslash
  64037. // http://pear.php.net/bugs/bug.php?id=19482
  64038. if ('@include_path@ ' != '@'.'include_path'.'@ ') {
  64039. ini_set('include_path', trim('@include_path@ '). PATH_SEPARATOR . get_include_path());
  64040. $raw = false;
  64041. } else {
  64042. // this is a raw, uninstalled pear, either a cvs checkout, or php distro
  64043. ini_set('include_path', dirname(__DIR__) . PATH_SEPARATOR . get_include_path());
  64044. $raw = true;
  64045. }
  64046. @ini_set('allow_url_fopen', true);
  64047. @set_time_limit(0);
  64048. ob_implicit_flush(true);
  64049. @ini_set('track_errors', true);
  64050. @ini_set('html_errors', false);
  64051. $_PEAR_PHPDIR = '#$%^&*';
  64052. set_error_handler('error_handler');
  64053. $pear_package_version = "@pear_version@";
  64054. require_once 'PEAR.php';
  64055. require_once 'PEAR/Frontend.php';
  64056. require_once 'PEAR/Config.php';
  64057. require_once 'PEAR/Command.php';
  64058. require_once 'Console/Getopt.php';
  64059. PEAR_Command::setFrontendType('CLI');
  64060. $all_commands = PEAR_Command::getCommands();
  64061. $argv = Console_Getopt::readPHPArgv();
  64062. // fix CGI sapi oddity - the -- in pear.bat/pear is not removed
  64063. if (php_sapi_name() != 'cli' && isset($argv[1]) && $argv[1] == '--') {
  64064. unset($argv[1]);
  64065. $argv = array_values($argv);
  64066. }
  64067. $progname = PEAR_RUNTYPE;
  64068. array_shift($argv);
  64069. $options = Console_Getopt::getopt2($argv, "c:C:d:D:Gh?sSqu:vV");
  64070. if (PEAR::isError($options)) {
  64071. usage($options);
  64072. }
  64073. $opts = $options[0];
  64074. $fetype = 'CLI';
  64075. if ($progname == 'gpear' || $progname == 'pear-gtk') {
  64076. $fetype = 'Gtk2';
  64077. } else {
  64078. foreach ($opts as $opt) {
  64079. if ($opt[0] == 'G') {
  64080. $fetype = 'Gtk2';
  64081. }
  64082. }
  64083. }
  64084. $pear_user_config = '';
  64085. $pear_system_config = '';
  64086. $store_user_config = false;
  64087. $store_system_config = false;
  64088. $verbose = 1;
  64089. foreach ($opts as $opt) {
  64090. switch ($opt[0]) {
  64091. case 'c':
  64092. $pear_user_config = $opt[1];
  64093. break;
  64094. case 'C':
  64095. $pear_system_config = $opt[1];
  64096. break;
  64097. }
  64098. }
  64099. PEAR_Command::setFrontendType($fetype);
  64100. $ui = &PEAR_Command::getFrontendObject();
  64101. $config = &PEAR_Config::singleton($pear_user_config, $pear_system_config);
  64102. if (PEAR::isError($config)) {
  64103. $_file = '';
  64104. if ($pear_user_config !== false) {
  64105. $_file .= $pear_user_config;
  64106. }
  64107. if ($pear_system_config !== false) {
  64108. $_file .= '/' . $pear_system_config;
  64109. }
  64110. if ($_file == '/') {
  64111. $_file = 'The default config file';
  64112. }
  64113. $config->getMessage();
  64114. $ui->outputData("ERROR: $_file is not a valid config file or is corrupted.");
  64115. // We stop, we have no idea where we are :)
  64116. exit(1);
  64117. }
  64118. // this is used in the error handler to retrieve a relative path
  64119. $_PEAR_PHPDIR = $config->get('php_dir');
  64120. $ui->setConfig($config);
  64121. PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, array($ui, "displayFatalError"));
  64122. $verbose = $config->get("verbose");
  64123. $cmdopts = array();
  64124. if ($raw) {
  64125. if (!$config->isDefinedLayer('user') && !$config->isDefinedLayer('system')) {
  64126. $found = false;
  64127. foreach ($opts as $opt) {
  64128. if ($opt[0] == 'd' || $opt[0] == 'D') {
  64129. // the user knows what they are doing, and are setting config values
  64130. $found = true;
  64131. }
  64132. }
  64133. if (!$found) {
  64134. // no prior runs, try to install PEAR
  64135. $parent = dirname(__FILE__);
  64136. if (strpos($parent, 'scripts')) {
  64137. $grandparent = dirname($parent);
  64138. $packagexml = $grandparent . DIRECTORY_SEPARATOR . 'package2.xml';
  64139. $pearbase = $grandparent;
  64140. } else {
  64141. $packagexml = $parent . DIRECTORY_SEPARATOR . 'package2.xml';
  64142. $pearbase = $parent;
  64143. }
  64144. if (file_exists($packagexml)) {
  64145. $options[1] = array(
  64146. 'install',
  64147. $packagexml
  64148. );
  64149. $config->set('php_dir', $pearbase . DIRECTORY_SEPARATOR . 'php');
  64150. $config->set('data_dir', $pearbase . DIRECTORY_SEPARATOR . 'data');
  64151. $config->set('doc_dir', $pearbase . DIRECTORY_SEPARATOR . 'docs');
  64152. $config->set('test_dir', $pearbase . DIRECTORY_SEPARATOR . 'tests');
  64153. $config->set(
  64154. 'ext_dir',
  64155. $pearbase . DIRECTORY_SEPARATOR . 'extensions'
  64156. );
  64157. $config->set('bin_dir', $pearbase);
  64158. $config->mergeConfigFile($pearbase . 'pear.ini', false);
  64159. $config->store();
  64160. $config->set('auto_discover', 1);
  64161. }
  64162. }
  64163. }
  64164. }
  64165. foreach ($opts as $opt) {
  64166. $param = !empty($opt[1]) ? $opt[1] : true;
  64167. switch ($opt[0]) {
  64168. case 'd':
  64169. if ($param === true) {
  64170. die(
  64171. 'Invalid usage of "-d" option, expected -d config_value=value, ' .
  64172. 'received "-d"' . "\n"
  64173. );
  64174. }
  64175. $possible = explode('=', $param);
  64176. if (count($possible) != 2) {
  64177. die(
  64178. 'Invalid usage of "-d" option, expected ' .
  64179. '-d config_value=value, received "' . $param . '"' . "\n"
  64180. );
  64181. }
  64182. list($key, $value) = explode('=', $param);
  64183. $config->set($key, $value, 'user');
  64184. break;
  64185. case 'D':
  64186. if ($param === true) {
  64187. die(
  64188. 'Invalid usage of "-d" option, expected ' .
  64189. '-d config_value=value, received "-d"' . "\n"
  64190. );
  64191. }
  64192. $possible = explode('=', $param);
  64193. if (count($possible) != 2) {
  64194. die(
  64195. 'Invalid usage of "-d" option, expected ' .
  64196. '-d config_value=value, received "' . $param . '"' . "\n"
  64197. );
  64198. }
  64199. list($key, $value) = explode('=', $param);
  64200. $config->set($key, $value, 'system');
  64201. break;
  64202. case 's':
  64203. $store_user_config = true;
  64204. break;
  64205. case 'S':
  64206. $store_system_config = true;
  64207. break;
  64208. case 'u':
  64209. $config->remove($param, 'user');
  64210. break;
  64211. case 'v':
  64212. $config->set('verbose', $config->get('verbose') + 1);
  64213. break;
  64214. case 'q':
  64215. $config->set('verbose', $config->get('verbose') - 1);
  64216. break;
  64217. case 'V':
  64218. usage(null, 'version');
  64219. case 'c':
  64220. case 'C':
  64221. break;
  64222. default:
  64223. // all non pear params goes to the command
  64224. $cmdopts[$opt[0]] = $param;
  64225. break;
  64226. }
  64227. }
  64228. if ($store_system_config) {
  64229. $config->store('system');
  64230. }
  64231. if ($store_user_config) {
  64232. $config->store('user');
  64233. }
  64234. $command = (isset($options[1][0])) ? $options[1][0] : null;
  64235. if (empty($command) && ($store_user_config || $store_system_config)) {
  64236. exit;
  64237. }
  64238. if ($fetype == 'Gtk2') {
  64239. if (!$config->validConfiguration()) {
  64240. PEAR::raiseError(
  64241. "CRITICAL ERROR: no existing valid configuration files found in " .
  64242. "files '$pear_user_config' or '$pear_system_config', " .
  64243. "please copy an existing configuration file to one of these " .
  64244. "locations, or use the -c and -s options to create one"
  64245. );
  64246. }
  64247. Gtk::main();
  64248. } else {
  64249. do {
  64250. if ($command == 'help') {
  64251. usage(null, isset($options[1][1]) ? $options[1][1] : null);
  64252. }
  64253. if (!$config->validConfiguration()) {
  64254. PEAR::raiseError(
  64255. "CRITICAL ERROR: no existing valid configuration files found " .
  64256. "in files '$pear_user_config' or '$pear_system_config', " .
  64257. "please copy an existing configuration file to one of " .
  64258. "these locations, or use the -c and -s options to create one"
  64259. );
  64260. }
  64261. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  64262. $cmd = PEAR_Command::factory($command, $config);
  64263. PEAR::popErrorHandling();
  64264. if (PEAR::isError($cmd)) {
  64265. usage(null, isset($options[1][0]) ? $options[1][0] : null);
  64266. }
  64267. $short_args = $long_args = null;
  64268. PEAR_Command::getGetoptArgs($command, $short_args, $long_args);
  64269. array_shift($options[1]);
  64270. $tmp = Console_Getopt::getopt2($options[1], $short_args, $long_args);
  64271. if (PEAR::isError($tmp)) {
  64272. break;
  64273. }
  64274. list($tmpopt, $params) = $tmp;
  64275. $opts = array();
  64276. foreach ($tmpopt as $foo => $tmp2) {
  64277. list($opt, $value) = $tmp2;
  64278. if ($value === null) {
  64279. $value = true; // options without args
  64280. }
  64281. if (strlen($opt) == 1) {
  64282. $cmdoptions = $cmd->getOptions($command);
  64283. foreach ($cmdoptions as $o => $d) {
  64284. if (isset($d['shortopt']) && $d['shortopt'] == $opt) {
  64285. $opts[$o] = $value;
  64286. }
  64287. }
  64288. } else {
  64289. if (substr($opt, 0, 2) == '--') {
  64290. $opts[substr($opt, 2)] = $value;
  64291. }
  64292. }
  64293. }
  64294. $ok = $cmd->run($command, $opts, $params);
  64295. if ($ok === false) {
  64296. PEAR::raiseError("unknown command `$command'");
  64297. }
  64298. if (PEAR::isError($ok)) {
  64299. PEAR::setErrorHandling(
  64300. PEAR_ERROR_CALLBACK, array($ui, "displayFatalError")
  64301. );
  64302. PEAR::raiseError($ok);
  64303. }
  64304. } while (false);
  64305. }
  64306. // {{{ usage()
  64307. /**
  64308. * Display usage information
  64309. *
  64310. * @param mixed $error Optional error message
  64311. * @param mixed $helpsubject Optional subject/command to display help for
  64312. *
  64313. * @return void
  64314. */
  64315. function usage($error = null, $helpsubject = null)
  64316. {
  64317. global $progname, $all_commands;
  64318. $stdout = fopen('php://stdout', 'w');
  64319. if (PEAR::isError($error)) {
  64320. fputs($stdout, $error->getMessage() . "\n");
  64321. } elseif ($error !== null) {
  64322. fputs($stdout, "$error\n");
  64323. }
  64324. if ($helpsubject != null) {
  64325. $put = cmdHelp($helpsubject);
  64326. } else {
  64327. $put = "Commands:\n";
  64328. $maxlen = max(array_map("strlen", $all_commands));
  64329. $formatstr = "%-{$maxlen}s %s\n";
  64330. ksort($all_commands);
  64331. foreach ($all_commands as $cmd => $class) {
  64332. $put .= sprintf($formatstr, $cmd, PEAR_Command::getDescription($cmd));
  64333. }
  64334. $put .=
  64335. "Usage: $progname [options] command [command-options] <parameters>\n".
  64336. "Type \"$progname help options\" to list all options.\n".
  64337. "Type \"$progname help shortcuts\" to list all command shortcuts.\n".
  64338. "Type \"$progname help version\" or ".
  64339. "\"$progname version\" to list version information.\n".
  64340. "Type \"$progname help <command>\" to get the help ".
  64341. "for the specified command.";
  64342. }
  64343. fputs($stdout, "$put\n");
  64344. fclose($stdout);
  64345. if ($error === null) {
  64346. exit(0);
  64347. }
  64348. exit(1);
  64349. }
  64350. /**
  64351. * Return help string for specified command
  64352. *
  64353. * @param string $command Command to return help for
  64354. *
  64355. * @return void
  64356. */
  64357. function cmdHelp($command)
  64358. {
  64359. global $progname, $all_commands, $config;
  64360. if ($command == "options") {
  64361. return
  64362. "Options:\n".
  64363. " -v increase verbosity level (default 1)\n".
  64364. " -q be quiet, decrease verbosity level\n".
  64365. " -c file find user configuration in `file'\n".
  64366. " -C file find system configuration in `file'\n".
  64367. " -d foo=bar set user config variable `foo' to `bar'\n".
  64368. " -D foo=bar set system config variable `foo' to `bar'\n".
  64369. " -G start in graphical (Gtk) mode\n".
  64370. " -s store user configuration\n".
  64371. " -S store system configuration\n".
  64372. " -u foo unset `foo' in the user configuration\n".
  64373. " -h, -? display help/usage (this message)\n".
  64374. " -V version information\n";
  64375. } elseif ($command == "shortcuts") {
  64376. $sc = PEAR_Command::getShortcuts();
  64377. $ret = "Shortcuts:\n";
  64378. foreach ($sc as $s => $c) {
  64379. $ret .= sprintf(" %-8s %s\n", $s, $c);
  64380. }
  64381. return $ret;
  64382. } elseif ($command == "version") {
  64383. return "PEAR Version: ".$GLOBALS['pear_package_version'].
  64384. "\nPHP Version: ".phpversion().
  64385. "\nZend Engine Version: ".zend_version().
  64386. "\nRunning on: ".php_uname();
  64387. } elseif ($help = PEAR_Command::getHelp($command)) {
  64388. if (is_string($help)) {
  64389. return "$progname $command [options] $help\n";
  64390. }
  64391. if ($help[1] === null) {
  64392. return "$progname $command $help[0]";
  64393. }
  64394. return "$progname $command [options] $help[0]\n$help[1]";
  64395. }
  64396. return "Command '$command' is not valid, try '$progname help'";
  64397. }
  64398. // }}}
  64399. /**
  64400. * error_handler
  64401. *
  64402. * @param mixed $errno Error number
  64403. * @param mixed $errmsg Message
  64404. * @param mixed $file Filename
  64405. * @param mixed $line Line number
  64406. *
  64407. * @access public
  64408. * @return boolean
  64409. */
  64410. function error_handler($errno, $errmsg, $file, $line)
  64411. {
  64412. if ((!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 80400) && ($errno & E_STRICT)) {
  64413. return; // E_STRICT
  64414. }
  64415. if ($errno & E_DEPRECATED) {
  64416. return; // E_DEPRECATED
  64417. }
  64418. if (!(error_reporting() & $errno) &&
  64419. isset($GLOBALS['config']) &&
  64420. $GLOBALS['config']->get('verbose') < 4
  64421. ) {
  64422. return false; // @silenced error, show all if debug is high enough
  64423. }
  64424. $errortype = array (
  64425. E_DEPRECATED => 'Deprecated Warning',
  64426. E_ERROR => "Error",
  64427. E_WARNING => "Warning",
  64428. E_PARSE => "Parsing Error",
  64429. E_NOTICE => "Notice",
  64430. E_CORE_ERROR => "Core Error",
  64431. E_CORE_WARNING => "Core Warning",
  64432. E_COMPILE_ERROR => "Compile Error",
  64433. E_COMPILE_WARNING => "Compile Warning",
  64434. E_USER_ERROR => "User Error",
  64435. E_USER_WARNING => "User Warning",
  64436. E_USER_NOTICE => "User Notice"
  64437. );
  64438. if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 80400) {
  64439. $errortype[E_STRICT] = 'Strict Warning';
  64440. }
  64441. $prefix = $errortype[$errno];
  64442. global $_PEAR_PHPDIR;
  64443. if (stristr($file, $_PEAR_PHPDIR)) {
  64444. $file = substr($file, strlen($_PEAR_PHPDIR) + 1);
  64445. } else {
  64446. $file = basename($file);
  64447. }
  64448. print "\n$prefix: $errmsg in $file on line $line\n";
  64449. return false;
  64450. }
  64451. /*
  64452. * Local variables:
  64453. * tab-width: 4
  64454. * c-basic-offset: 4
  64455. * indent-tabs-mode: nil
  64456. * mode: php
  64457. * End:
  64458. */
  64459. // vim600:syn=php
  64460. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/scripts/peclcmd.php��������������������������������������������������������������������0000664�0001750�0001750�00000002105�14720722517�016075� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  64461. /**
  64462. * PEAR, the PHP Extension and Application Repository
  64463. *
  64464. * Command line interface
  64465. *
  64466. * PHP versions 4 and 5
  64467. *
  64468. * @category pear
  64469. * @package PEAR
  64470. * @author Stig Bakken <ssb@php.net>
  64471. * @author Tomas V.V.Cox <cox@idecnet.com>
  64472. * @copyright 1997-2009 The Authors
  64473. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  64474. * @link http://pear.php.net/package/PEAR
  64475. */
  64476. /**
  64477. * @nodep Gtk
  64478. */
  64479. //the space is needed for windows include paths with trailing backslash
  64480. // http://pear.php.net/bugs/bug.php?id=19482
  64481. if ('@include_path@ ' != '@'.'include_path'.'@ ') {
  64482. ini_set('include_path', trim('@include_path@ '). PATH_SEPARATOR . get_include_path());
  64483. $raw = false;
  64484. } else {
  64485. // this is a raw, uninstalled pear, either a cvs checkout, or php distro
  64486. ini_set('include_path', __DIR__ . PATH_SEPARATOR . get_include_path());
  64487. $raw = true;
  64488. }
  64489. define('PEAR_RUNTYPE', 'pecl');
  64490. require_once 'pearcmd.php';
  64491. /*
  64492. * Local variables:
  64493. * tab-width: 4
  64494. * c-basic-offset: 4
  64495. * indent-tabs-mode: nil
  64496. * mode: php
  64497. * End:
  64498. */
  64499. // vim600:syn=php
  64500. ?>
  64501. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/LICENSE��������������������������������������������������������������������������������0000644�0001750�0001750�00000002705�14720722517�013277� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Copyright (c) 1997-2009,
  64502. Stig Bakken <ssb@php.net>,
  64503. Gregory Beaver <cellog@php.net>,
  64504. Helgi Þormar Þorbjörnsson <helgi@php.net>,
  64505. Tomas V.V.Cox <cox@idecnet.com>,
  64506. Martin Jansen <mj@php.net>.
  64507. All rights reserved.
  64508. Redistribution and use in source and binary forms, with or without
  64509. modification, are permitted provided that the following conditions are met:
  64510. * Redistributions of source code must retain the above copyright notice,
  64511. this list of conditions and the following disclaimer.
  64512. * Redistributions in binary form must reproduce the above copyright
  64513. notice, this list of conditions and the following disclaimer in the
  64514. documentation and/or other materials provided with the distribution.
  64515. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  64516. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  64517. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  64518. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
  64519. FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  64520. DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  64521. SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  64522. CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  64523. OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  64524. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  64525. �����������������������������������������������������������PEAR-1.10.16/INSTALL��������������������������������������������������������������������������������0000644�0001750�0001750�00000004170�14720722517�013321� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR - The PEAR Installer
  64526. =========================
  64527. Installing the PEAR Installer.
  64528. You should install PEAR on a local development machine first. Installing
  64529. PEAR on a remote production machine should only be done after you are
  64530. familiar with PEAR and have tested code using PEAR on your development
  64531. machine.
  64532. There are two methods of installing PEAR
  64533. - PEAR bundled in PHP
  64534. - go-pear
  64535. We will first examine how to install PEAR that is bundled with PHP.
  64536. Microsoft Windows
  64537. =================
  64538. If you are running PHP 5.2.0 or newer, simply download and
  64539. run the windows installer (.msi) and PEAR can be automatically
  64540. installed.
  64541. Otherwise, for older PHP versions, download the .zip of windows,
  64542. there is a script included with your PHP distribution that is called
  64543. "go-pear". You must open a command box in order to run it. Click
  64544. "start" then click "Run..." and type "cmd.exe" to open a command box.
  64545. Use "cd" to change directory to the location of PHP where you unzipped it,
  64546. and run the go-pear command.
  64547. Unix
  64548. ====
  64549. When compiling PHP from source, you simply need to include the
  64550. --with-pear directive on the "./configure" command. This is "on"
  64551. by default in most PHP versions, but it doesn't hurt to list it
  64552. explicitly. You should also consider enabling the zlib extension via
  64553. --enable-zlib, so that the PEAR installer will be able to handle gzipped
  64554. files (i.e. smaller package files for faster downloads). Later, when you
  64555. run "make install" to install PHP itself, part of the process will be
  64556. prompts that ask you where you want PEAR to be installed.
  64557. go-pear
  64558. =======
  64559. For users who cannot perform the above steps, or who wish to obtain the
  64560. latest PEAR with a slightly higher risk of failure, use go-pear. go-pear
  64561. is obtained by downloading http://pear.php.net/go-pear and saving it as go-pear.php.
  64562. After downloading, simply run "php go-pear.php" or open it in a web browser
  64563. (windows only) to download and install PEAR.
  64564. You can always ask general installation questions on pear-general@lists.php.net,
  64565. a public mailing list devoted to support for PEAR packages and installation-
  64566. related issues.
  64567. Happy PHPing, we hope PEAR will be a great tool for your development work!
  64568. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/package.dtd����������������������������������������������������������������������������0000664�0001750�0001750�00000006404�14720722517�014364� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!--
  64569. This is the PEAR package description, version 1.0.
  64570. It should be used with the informal public identifier:
  64571. "-//PHP Group//DTD PEAR Package 1.0//EN//XML"
  64572. Copyright (c) 1997-2005 The PHP Group
  64573. This source file is subject to the New BSD License,
  64574. that is bundled with this package in the file LICENSE, and is
  64575. available at through the world-wide-web at
  64576. http://opensource.org/licenses/bsd-license.php.
  64577. If you did not receive a copy of the New BSD License and are unable to
  64578. obtain it through the world-wide-web, please send a note to
  64579. license@php.net so we can mail you a copy immediately.
  64580. Authors:
  64581. Stig S. Bakken <ssb@fast.no>
  64582. Gregory Beaver <cellog@php.net>
  64583. -->
  64584. <!ENTITY % NUMBER "CDATA">
  64585. <!ELEMENT package (name,summary,description,license?,maintainers,release,changelog?)>
  64586. <!ATTLIST package type (source|binary|empty) "empty"
  64587. version CDATA #REQUIRED
  64588. packagerversion CDATA #IMPLIED>
  64589. <!ELEMENT name (#PCDATA)>
  64590. <!ELEMENT summary (#PCDATA)>
  64591. <!ELEMENT license (#PCDATA)>
  64592. <!ELEMENT description (#PCDATA)>
  64593. <!ELEMENT maintainers (maintainer)+>
  64594. <!ELEMENT maintainer (user|role|name|email)+>
  64595. <!ELEMENT user (#PCDATA)>
  64596. <!ELEMENT role (#PCDATA)>
  64597. <!ELEMENT email (#PCDATA)>
  64598. <!ELEMENT changelog (release)+>
  64599. <!ELEMENT release (version,date,license,state,notes,warnings?,provides*,deps?,configureoptions?,filelist?)>
  64600. <!ELEMENT version (#PCDATA)>
  64601. <!ELEMENT date (#PCDATA)>
  64602. <!ELEMENT state (#PCDATA)>
  64603. <!ELEMENT notes (#PCDATA)>
  64604. <!ELEMENT warnings (#PCDATA)>
  64605. <!ELEMENT deps (dep*)>
  64606. <!ELEMENT dep (#PCDATA)>
  64607. <!ATTLIST dep type (pkg|ext|php) #REQUIRED
  64608. rel (has|eq|lt|le|gt|ge) #IMPLIED
  64609. version CDATA #IMPLIED
  64610. optional (yes|no) 'no'>
  64611. <!ELEMENT configureoptions (configureoption)+>
  64612. <!ELEMENT configureoption EMPTY>
  64613. <!ATTLIST configureoption name CDATA #REQUIRED
  64614. default CDATA #IMPLIED
  64615. prompt CDATA #REQUIRED>
  64616. <!ELEMENT provides EMPTY>
  64617. <!ATTLIST provides type (ext|prog|class|function|feature|api) #REQUIRED
  64618. name CDATA #REQUIRED
  64619. extends CDATA #IMPLIED>
  64620. <!ELEMENT filelist (dir|file)+>
  64621. <!ELEMENT dir (dir|file)+>
  64622. <!ATTLIST dir name CDATA #REQUIRED
  64623. role (php|ext|src|test|doc|data|script) 'php'
  64624. baseinstalldir CDATA #IMPLIED>
  64625. <!ELEMENT file (replace*)>
  64626. <!ATTLIST file role (php|ext|src|test|doc|data|script) 'php'
  64627. debug (na|on|off) 'na'
  64628. format CDATA #IMPLIED
  64629. baseinstalldir CDATA #IMPLIED
  64630. platform CDATA #IMPLIED
  64631. md5sum CDATA #IMPLIED
  64632. name CDATA #REQUIRED
  64633. install-as CDATA #IMPLIED>
  64634. <!ELEMENT replace EMPTY>
  64635. <!ATTLIST replace type (php-const|pear-config|package-info) #REQUIRED
  64636. from CDATA #REQUIRED
  64637. to CDATA #REQUIRED>
  64638. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/PEAR.php�������������������������������������������������������������������������������0000664�0001750�0001750�00000106624�14720722517�013541� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  64639. /**
  64640. * PEAR, the PHP Extension and Application Repository
  64641. *
  64642. * PEAR class and PEAR_Error class
  64643. *
  64644. * PHP versions 4 and 5
  64645. *
  64646. * @category pear
  64647. * @package PEAR
  64648. * @author Sterling Hughes <sterling@php.net>
  64649. * @author Stig Bakken <ssb@php.net>
  64650. * @author Tomas V.V.Cox <cox@idecnet.com>
  64651. * @author Greg Beaver <cellog@php.net>
  64652. * @copyright 1997-2010 The Authors
  64653. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  64654. * @link http://pear.php.net/package/PEAR
  64655. * @since File available since Release 0.1
  64656. */
  64657. /**#@+
  64658. * ERROR constants
  64659. */
  64660. define('PEAR_ERROR_RETURN', 1);
  64661. define('PEAR_ERROR_PRINT', 2);
  64662. define('PEAR_ERROR_TRIGGER', 4);
  64663. define('PEAR_ERROR_DIE', 8);
  64664. define('PEAR_ERROR_CALLBACK', 16);
  64665. /**
  64666. * WARNING: obsolete
  64667. * @deprecated
  64668. */
  64669. define('PEAR_ERROR_EXCEPTION', 32);
  64670. /**#@-*/
  64671. if (substr(PHP_OS, 0, 3) == 'WIN') {
  64672. define('OS_WINDOWS', true);
  64673. define('OS_UNIX', false);
  64674. define('PEAR_OS', 'Windows');
  64675. } else {
  64676. define('OS_WINDOWS', false);
  64677. define('OS_UNIX', true);
  64678. define('PEAR_OS', 'Unix'); // blatant assumption
  64679. }
  64680. $GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN;
  64681. $GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE;
  64682. $GLOBALS['_PEAR_destructor_object_list'] = array();
  64683. $GLOBALS['_PEAR_shutdown_funcs'] = array();
  64684. $GLOBALS['_PEAR_error_handler_stack'] = array();
  64685. if(function_exists('ini_set')) {
  64686. @ini_set('track_errors', true);
  64687. }
  64688. /**
  64689. * Base class for other PEAR classes. Provides rudimentary
  64690. * emulation of destructors.
  64691. *
  64692. * If you want a destructor in your class, inherit PEAR and make a
  64693. * destructor method called _yourclassname (same name as the
  64694. * constructor, but with a "_" prefix). Also, in your constructor you
  64695. * have to call the PEAR constructor: $this->PEAR();.
  64696. * The destructor method will be called without parameters. Note that
  64697. * at in some SAPI implementations (such as Apache), any output during
  64698. * the request shutdown (in which destructors are called) seems to be
  64699. * discarded. If you need to get any debug information from your
  64700. * destructor, use error_log(), syslog() or something similar.
  64701. *
  64702. * IMPORTANT! To use the emulated destructors you need to create the
  64703. * objects by reference: $obj =& new PEAR_child;
  64704. *
  64705. * @category pear
  64706. * @package PEAR
  64707. * @author Stig Bakken <ssb@php.net>
  64708. * @author Tomas V.V. Cox <cox@idecnet.com>
  64709. * @author Greg Beaver <cellog@php.net>
  64710. * @copyright 1997-2006 The PHP Group
  64711. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  64712. * @version Release: 1.10.16
  64713. * @link http://pear.php.net/package/PEAR
  64714. * @see PEAR_Error
  64715. * @since Class available since PHP 4.0.2
  64716. * @link http://pear.php.net/manual/en/core.pear.php#core.pear.pear
  64717. */
  64718. class PEAR
  64719. {
  64720. /**
  64721. * Whether to enable internal debug messages.
  64722. *
  64723. * @var bool
  64724. * @access private
  64725. */
  64726. var $_debug = false;
  64727. /**
  64728. * Default error mode for this object.
  64729. *
  64730. * @var int
  64731. * @access private
  64732. */
  64733. var $_default_error_mode = null;
  64734. /**
  64735. * Default error options used for this object when error mode
  64736. * is PEAR_ERROR_TRIGGER.
  64737. *
  64738. * @var int
  64739. * @access private
  64740. */
  64741. var $_default_error_options = null;
  64742. /**
  64743. * Default error handler (callback) for this object, if error mode is
  64744. * PEAR_ERROR_CALLBACK.
  64745. *
  64746. * @var string
  64747. * @access private
  64748. */
  64749. var $_default_error_handler = '';
  64750. /**
  64751. * Which class to use for error objects.
  64752. *
  64753. * @var string
  64754. * @access private
  64755. */
  64756. var $_error_class = 'PEAR_Error';
  64757. /**
  64758. * An array of expected errors.
  64759. *
  64760. * @var array
  64761. * @access private
  64762. */
  64763. var $_expected_errors = array();
  64764. /**
  64765. * List of methods that can be called both statically and non-statically.
  64766. * @var array
  64767. */
  64768. protected static $bivalentMethods = array(
  64769. 'setErrorHandling' => true,
  64770. 'raiseError' => true,
  64771. 'throwError' => true,
  64772. 'pushErrorHandling' => true,
  64773. 'popErrorHandling' => true,
  64774. );
  64775. /**
  64776. * Constructor. Registers this object in
  64777. * $_PEAR_destructor_object_list for destructor emulation if a
  64778. * destructor object exists.
  64779. *
  64780. * @param string $error_class (optional) which class to use for
  64781. * error objects, defaults to PEAR_Error.
  64782. * @access public
  64783. * @return void
  64784. */
  64785. function __construct($error_class = null)
  64786. {
  64787. $classname = strtolower(get_class($this));
  64788. if ($this->_debug) {
  64789. print "PEAR constructor called, class=$classname\n";
  64790. }
  64791. if ($error_class !== null) {
  64792. $this->_error_class = $error_class;
  64793. }
  64794. while ($classname && strcasecmp($classname, "pear")) {
  64795. $destructor = "_$classname";
  64796. if (method_exists($this, $destructor)) {
  64797. global $_PEAR_destructor_object_list;
  64798. $_PEAR_destructor_object_list[] = $this;
  64799. if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
  64800. register_shutdown_function("_PEAR_call_destructors");
  64801. $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
  64802. }
  64803. break;
  64804. } else {
  64805. $classname = get_parent_class($classname);
  64806. }
  64807. }
  64808. }
  64809. /**
  64810. * Only here for backwards compatibility.
  64811. * E.g. Archive_Tar calls $this->PEAR() in its constructor.
  64812. *
  64813. * @param string $error_class Which class to use for error objects,
  64814. * defaults to PEAR_Error.
  64815. */
  64816. public function PEAR($error_class = null)
  64817. {
  64818. self::__construct($error_class);
  64819. }
  64820. /**
  64821. * Destructor (the emulated type of...). Does nothing right now,
  64822. * but is included for forward compatibility, so subclass
  64823. * destructors should always call it.
  64824. *
  64825. * See the note in the class desciption about output from
  64826. * destructors.
  64827. *
  64828. * @access public
  64829. * @return void
  64830. */
  64831. function _PEAR() {
  64832. if ($this->_debug) {
  64833. printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
  64834. }
  64835. }
  64836. public function __call($method, $arguments)
  64837. {
  64838. if (!isset(self::$bivalentMethods[$method])) {
  64839. trigger_error(
  64840. 'Call to undefined method PEAR::' . $method . '()', E_USER_ERROR
  64841. );
  64842. }
  64843. return call_user_func_array(
  64844. array(__CLASS__, '_' . $method),
  64845. array_merge(array($this), $arguments)
  64846. );
  64847. }
  64848. public static function __callStatic($method, $arguments)
  64849. {
  64850. if (!isset(self::$bivalentMethods[$method])) {
  64851. trigger_error(
  64852. 'Call to undefined method PEAR::' . $method . '()', E_USER_ERROR
  64853. );
  64854. }
  64855. return call_user_func_array(
  64856. array(__CLASS__, '_' . $method),
  64857. array_merge(array(null), $arguments)
  64858. );
  64859. }
  64860. /**
  64861. * If you have a class that's mostly/entirely static, and you need static
  64862. * properties, you can use this method to simulate them. Eg. in your method(s)
  64863. * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
  64864. * You MUST use a reference, or they will not persist!
  64865. *
  64866. * @param string $class The calling classname, to prevent clashes
  64867. * @param string $var The variable to retrieve.
  64868. * @return mixed A reference to the variable. If not set it will be
  64869. * auto initialised to NULL.
  64870. */
  64871. public static function &getStaticProperty($class, $var)
  64872. {
  64873. static $properties;
  64874. if (!isset($properties[$class])) {
  64875. $properties[$class] = array();
  64876. }
  64877. if (!array_key_exists($var, $properties[$class])) {
  64878. $properties[$class][$var] = null;
  64879. }
  64880. return $properties[$class][$var];
  64881. }
  64882. /**
  64883. * Use this function to register a shutdown method for static
  64884. * classes.
  64885. *
  64886. * @param mixed $func The function name (or array of class/method) to call
  64887. * @param mixed $args The arguments to pass to the function
  64888. *
  64889. * @return void
  64890. */
  64891. public static function registerShutdownFunc($func, $args = array())
  64892. {
  64893. // if we are called statically, there is a potential
  64894. // that no shutdown func is registered. Bug #6445
  64895. if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
  64896. register_shutdown_function("_PEAR_call_destructors");
  64897. $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
  64898. }
  64899. $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
  64900. }
  64901. /**
  64902. * Tell whether a value is a PEAR error.
  64903. *
  64904. * @param mixed $data the value to test
  64905. * @param int $code if $data is an error object, return true
  64906. * only if $code is a string and
  64907. * $obj->getMessage() == $code or
  64908. * $code is an integer and $obj->getCode() == $code
  64909. *
  64910. * @return bool true if parameter is an error
  64911. */
  64912. public static function isError($data, $code = null)
  64913. {
  64914. if (!is_a($data, 'PEAR_Error')) {
  64915. return false;
  64916. }
  64917. if (is_null($code)) {
  64918. return true;
  64919. } elseif (is_string($code)) {
  64920. return $data->getMessage() == $code;
  64921. }
  64922. return $data->getCode() == $code;
  64923. }
  64924. /**
  64925. * Sets how errors generated by this object should be handled.
  64926. * Can be invoked both in objects and statically. If called
  64927. * statically, setErrorHandling sets the default behaviour for all
  64928. * PEAR objects. If called in an object, setErrorHandling sets
  64929. * the default behaviour for that object.
  64930. *
  64931. * @param object $object
  64932. * Object the method was called on (non-static mode)
  64933. *
  64934. * @param int $mode
  64935. * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
  64936. * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
  64937. * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
  64938. *
  64939. * @param mixed $options
  64940. * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
  64941. * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
  64942. *
  64943. * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
  64944. * to be the callback function or method. A callback
  64945. * function is a string with the name of the function, a
  64946. * callback method is an array of two elements: the element
  64947. * at index 0 is the object, and the element at index 1 is
  64948. * the name of the method to call in the object.
  64949. *
  64950. * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
  64951. * a printf format string used when printing the error
  64952. * message.
  64953. *
  64954. * @access public
  64955. * @return void
  64956. * @see PEAR_ERROR_RETURN
  64957. * @see PEAR_ERROR_PRINT
  64958. * @see PEAR_ERROR_TRIGGER
  64959. * @see PEAR_ERROR_DIE
  64960. * @see PEAR_ERROR_CALLBACK
  64961. * @see PEAR_ERROR_EXCEPTION
  64962. *
  64963. * @since PHP 4.0.5
  64964. */
  64965. protected static function _setErrorHandling(
  64966. $object, $mode = null, $options = null
  64967. ) {
  64968. if ($object !== null) {
  64969. $setmode = &$object->_default_error_mode;
  64970. $setoptions = &$object->_default_error_options;
  64971. } else {
  64972. $setmode = &$GLOBALS['_PEAR_default_error_mode'];
  64973. $setoptions = &$GLOBALS['_PEAR_default_error_options'];
  64974. }
  64975. switch ($mode) {
  64976. case PEAR_ERROR_EXCEPTION:
  64977. case PEAR_ERROR_RETURN:
  64978. case PEAR_ERROR_PRINT:
  64979. case PEAR_ERROR_TRIGGER:
  64980. case PEAR_ERROR_DIE:
  64981. case null:
  64982. $setmode = $mode;
  64983. $setoptions = $options;
  64984. break;
  64985. case PEAR_ERROR_CALLBACK:
  64986. $setmode = $mode;
  64987. // class/object method callback
  64988. if (is_callable($options)) {
  64989. $setoptions = $options;
  64990. } else {
  64991. trigger_error("invalid error callback", E_USER_WARNING);
  64992. }
  64993. break;
  64994. default:
  64995. trigger_error("invalid error mode", E_USER_WARNING);
  64996. break;
  64997. }
  64998. }
  64999. /**
  65000. * This method is used to tell which errors you expect to get.
  65001. * Expected errors are always returned with error mode
  65002. * PEAR_ERROR_RETURN. Expected error codes are stored in a stack,
  65003. * and this method pushes a new element onto it. The list of
  65004. * expected errors are in effect until they are popped off the
  65005. * stack with the popExpect() method.
  65006. *
  65007. * Note that this method can not be called statically
  65008. *
  65009. * @param mixed $code a single error code or an array of error codes to expect
  65010. *
  65011. * @return int the new depth of the "expected errors" stack
  65012. * @access public
  65013. */
  65014. function expectError($code = '*')
  65015. {
  65016. if (is_array($code)) {
  65017. array_push($this->_expected_errors, $code);
  65018. } else {
  65019. array_push($this->_expected_errors, array($code));
  65020. }
  65021. return count($this->_expected_errors);
  65022. }
  65023. /**
  65024. * This method pops one element off the expected error codes
  65025. * stack.
  65026. *
  65027. * @return array the list of error codes that were popped
  65028. */
  65029. function popExpect()
  65030. {
  65031. return array_pop($this->_expected_errors);
  65032. }
  65033. /**
  65034. * This method checks unsets an error code if available
  65035. *
  65036. * @param mixed error code
  65037. * @return bool true if the error code was unset, false otherwise
  65038. * @access private
  65039. * @since PHP 4.3.0
  65040. */
  65041. function _checkDelExpect($error_code)
  65042. {
  65043. $deleted = false;
  65044. foreach ($this->_expected_errors as $key => $error_array) {
  65045. if (in_array($error_code, $error_array)) {
  65046. unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
  65047. $deleted = true;
  65048. }
  65049. // clean up empty arrays
  65050. if (0 == count($this->_expected_errors[$key])) {
  65051. unset($this->_expected_errors[$key]);
  65052. }
  65053. }
  65054. return $deleted;
  65055. }
  65056. /**
  65057. * This method deletes all occurrences of the specified element from
  65058. * the expected error codes stack.
  65059. *
  65060. * @param mixed $error_code error code that should be deleted
  65061. * @return mixed list of error codes that were deleted or error
  65062. * @access public
  65063. * @since PHP 4.3.0
  65064. */
  65065. function delExpect($error_code)
  65066. {
  65067. $deleted = false;
  65068. if ((is_array($error_code) && (0 != count($error_code)))) {
  65069. // $error_code is a non-empty array here; we walk through it trying
  65070. // to unset all values
  65071. foreach ($error_code as $key => $error) {
  65072. $deleted = $this->_checkDelExpect($error) ? true : false;
  65073. }
  65074. return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
  65075. } elseif (!empty($error_code)) {
  65076. // $error_code comes alone, trying to unset it
  65077. if ($this->_checkDelExpect($error_code)) {
  65078. return true;
  65079. }
  65080. return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
  65081. }
  65082. // $error_code is empty
  65083. return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
  65084. }
  65085. /**
  65086. * This method is a wrapper that returns an instance of the
  65087. * configured error class with this object's default error
  65088. * handling applied. If the $mode and $options parameters are not
  65089. * specified, the object's defaults are used.
  65090. *
  65091. * @param mixed $message a text error message or a PEAR error object
  65092. *
  65093. * @param int $code a numeric error code (it is up to your class
  65094. * to define these if you want to use codes)
  65095. *
  65096. * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
  65097. * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
  65098. * PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
  65099. *
  65100. * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
  65101. * specifies the PHP-internal error level (one of
  65102. * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
  65103. * If $mode is PEAR_ERROR_CALLBACK, this
  65104. * parameter specifies the callback function or
  65105. * method. In other error modes this parameter
  65106. * is ignored.
  65107. *
  65108. * @param string $userinfo If you need to pass along for example debug
  65109. * information, this parameter is meant for that.
  65110. *
  65111. * @param string $error_class The returned error object will be
  65112. * instantiated from this class, if specified.
  65113. *
  65114. * @param bool $skipmsg If true, raiseError will only pass error codes,
  65115. * the error message parameter will be dropped.
  65116. *
  65117. * @return object a PEAR error object
  65118. * @see PEAR::setErrorHandling
  65119. * @since PHP 4.0.5
  65120. */
  65121. protected static function _raiseError($object,
  65122. $message = null,
  65123. $code = null,
  65124. $mode = null,
  65125. $options = null,
  65126. $userinfo = null,
  65127. $error_class = null,
  65128. $skipmsg = false)
  65129. {
  65130. // The error is yet a PEAR error object
  65131. if (is_object($message)) {
  65132. $code = $message->getCode();
  65133. $userinfo = $message->getUserInfo();
  65134. $error_class = $message->getType();
  65135. $message->error_message_prefix = '';
  65136. $message = $message->getMessage();
  65137. }
  65138. if (
  65139. $object !== null &&
  65140. isset($object->_expected_errors) &&
  65141. count($object->_expected_errors) > 0 &&
  65142. count($exp = end($object->_expected_errors))
  65143. ) {
  65144. if ($exp[0] === "*" ||
  65145. (is_int(reset($exp)) && in_array($code, $exp)) ||
  65146. (is_string(reset($exp)) && in_array($message, $exp))
  65147. ) {
  65148. $mode = PEAR_ERROR_RETURN;
  65149. }
  65150. }
  65151. // No mode given, try global ones
  65152. if ($mode === null) {
  65153. // Class error handler
  65154. if ($object !== null && isset($object->_default_error_mode)) {
  65155. $mode = $object->_default_error_mode;
  65156. $options = $object->_default_error_options;
  65157. // Global error handler
  65158. } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
  65159. $mode = $GLOBALS['_PEAR_default_error_mode'];
  65160. $options = $GLOBALS['_PEAR_default_error_options'];
  65161. }
  65162. }
  65163. if ($error_class !== null) {
  65164. $ec = $error_class;
  65165. } elseif ($object !== null && isset($object->_error_class)) {
  65166. $ec = $object->_error_class;
  65167. } else {
  65168. $ec = 'PEAR_Error';
  65169. }
  65170. if ($skipmsg) {
  65171. $a = new $ec($code, $mode, $options, $userinfo);
  65172. } else {
  65173. $a = new $ec($message, $code, $mode, $options, $userinfo);
  65174. }
  65175. return $a;
  65176. }
  65177. /**
  65178. * Simpler form of raiseError with fewer options. In most cases
  65179. * message, code and userinfo are enough.
  65180. *
  65181. * @param mixed $message a text error message or a PEAR error object
  65182. *
  65183. * @param int $code a numeric error code (it is up to your class
  65184. * to define these if you want to use codes)
  65185. *
  65186. * @param string $userinfo If you need to pass along for example debug
  65187. * information, this parameter is meant for that.
  65188. *
  65189. * @return object a PEAR error object
  65190. * @see PEAR::raiseError
  65191. */
  65192. protected static function _throwError($object, $message = null, $code = null, $userinfo = null)
  65193. {
  65194. if ($object !== null) {
  65195. $a = $object->raiseError($message, $code, null, null, $userinfo);
  65196. return $a;
  65197. }
  65198. $a = PEAR::raiseError($message, $code, null, null, $userinfo);
  65199. return $a;
  65200. }
  65201. public static function staticPushErrorHandling($mode, $options = null)
  65202. {
  65203. $stack = &$GLOBALS['_PEAR_error_handler_stack'];
  65204. $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
  65205. $def_options = &$GLOBALS['_PEAR_default_error_options'];
  65206. $stack[] = array($def_mode, $def_options);
  65207. switch ($mode) {
  65208. case PEAR_ERROR_EXCEPTION:
  65209. case PEAR_ERROR_RETURN:
  65210. case PEAR_ERROR_PRINT:
  65211. case PEAR_ERROR_TRIGGER:
  65212. case PEAR_ERROR_DIE:
  65213. case null:
  65214. $def_mode = $mode;
  65215. $def_options = $options;
  65216. break;
  65217. case PEAR_ERROR_CALLBACK:
  65218. $def_mode = $mode;
  65219. // class/object method callback
  65220. if (is_callable($options)) {
  65221. $def_options = $options;
  65222. } else {
  65223. trigger_error("invalid error callback", E_USER_WARNING);
  65224. }
  65225. break;
  65226. default:
  65227. trigger_error("invalid error mode", E_USER_WARNING);
  65228. break;
  65229. }
  65230. $stack[] = array($mode, $options);
  65231. return true;
  65232. }
  65233. public static function staticPopErrorHandling()
  65234. {
  65235. $stack = &$GLOBALS['_PEAR_error_handler_stack'];
  65236. $setmode = &$GLOBALS['_PEAR_default_error_mode'];
  65237. $setoptions = &$GLOBALS['_PEAR_default_error_options'];
  65238. array_pop($stack);
  65239. list($mode, $options) = $stack[sizeof($stack) - 1];
  65240. array_pop($stack);
  65241. switch ($mode) {
  65242. case PEAR_ERROR_EXCEPTION:
  65243. case PEAR_ERROR_RETURN:
  65244. case PEAR_ERROR_PRINT:
  65245. case PEAR_ERROR_TRIGGER:
  65246. case PEAR_ERROR_DIE:
  65247. case null:
  65248. $setmode = $mode;
  65249. $setoptions = $options;
  65250. break;
  65251. case PEAR_ERROR_CALLBACK:
  65252. $setmode = $mode;
  65253. // class/object method callback
  65254. if (is_callable($options)) {
  65255. $setoptions = $options;
  65256. } else {
  65257. trigger_error("invalid error callback", E_USER_WARNING);
  65258. }
  65259. break;
  65260. default:
  65261. trigger_error("invalid error mode", E_USER_WARNING);
  65262. break;
  65263. }
  65264. return true;
  65265. }
  65266. /**
  65267. * Push a new error handler on top of the error handler options stack. With this
  65268. * you can easily override the actual error handler for some code and restore
  65269. * it later with popErrorHandling.
  65270. *
  65271. * @param mixed $mode (same as setErrorHandling)
  65272. * @param mixed $options (same as setErrorHandling)
  65273. *
  65274. * @return bool Always true
  65275. *
  65276. * @see PEAR::setErrorHandling
  65277. */
  65278. protected static function _pushErrorHandling($object, $mode, $options = null)
  65279. {
  65280. $stack = &$GLOBALS['_PEAR_error_handler_stack'];
  65281. if ($object !== null) {
  65282. $def_mode = &$object->_default_error_mode;
  65283. $def_options = &$object->_default_error_options;
  65284. } else {
  65285. $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
  65286. $def_options = &$GLOBALS['_PEAR_default_error_options'];
  65287. }
  65288. $stack[] = array($def_mode, $def_options);
  65289. if ($object !== null) {
  65290. $object->setErrorHandling($mode, $options);
  65291. } else {
  65292. PEAR::setErrorHandling($mode, $options);
  65293. }
  65294. $stack[] = array($mode, $options);
  65295. return true;
  65296. }
  65297. /**
  65298. * Pop the last error handler used
  65299. *
  65300. * @return bool Always true
  65301. *
  65302. * @see PEAR::pushErrorHandling
  65303. */
  65304. protected static function _popErrorHandling($object)
  65305. {
  65306. $stack = &$GLOBALS['_PEAR_error_handler_stack'];
  65307. array_pop($stack);
  65308. list($mode, $options) = $stack[sizeof($stack) - 1];
  65309. array_pop($stack);
  65310. if ($object !== null) {
  65311. $object->setErrorHandling($mode, $options);
  65312. } else {
  65313. PEAR::setErrorHandling($mode, $options);
  65314. }
  65315. return true;
  65316. }
  65317. /**
  65318. * OS independent PHP extension load. Remember to take care
  65319. * on the correct extension name for case sensitive OSes.
  65320. *
  65321. * @param string $ext The extension name
  65322. * @return bool Success or not on the dl() call
  65323. */
  65324. public static function loadExtension($ext)
  65325. {
  65326. if (extension_loaded($ext)) {
  65327. return true;
  65328. }
  65329. // if either returns true dl() will produce a FATAL error, stop that
  65330. if (
  65331. function_exists('dl') === false ||
  65332. ini_get('enable_dl') != 1
  65333. ) {
  65334. return false;
  65335. }
  65336. if (OS_WINDOWS) {
  65337. $suffix = '.dll';
  65338. } elseif (PHP_OS == 'HP-UX') {
  65339. $suffix = '.sl';
  65340. } elseif (PHP_OS == 'AIX') {
  65341. $suffix = '.a';
  65342. } elseif (PHP_OS == 'OSX') {
  65343. $suffix = '.bundle';
  65344. } else {
  65345. $suffix = '.so';
  65346. }
  65347. return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
  65348. }
  65349. /**
  65350. * Get SOURCE_DATE_EPOCH environment variable
  65351. * See https://reproducible-builds.org/specs/source-date-epoch/
  65352. *
  65353. * @return int
  65354. * @access public
  65355. */
  65356. static function getSourceDateEpoch()
  65357. {
  65358. if ($source_date_epoch = getenv('SOURCE_DATE_EPOCH')) {
  65359. if (preg_match('/^\d+$/', $source_date_epoch)) {
  65360. return (int) $source_date_epoch;
  65361. } else {
  65362. // "If the value is malformed, the build process SHOULD exit with a non-zero error code."
  65363. self::raiseError("Invalid SOURCE_DATE_EPOCH: $source_date_epoch");
  65364. exit(1);
  65365. }
  65366. } else {
  65367. return time();
  65368. }
  65369. }
  65370. }
  65371. function _PEAR_call_destructors()
  65372. {
  65373. global $_PEAR_destructor_object_list;
  65374. if (is_array($_PEAR_destructor_object_list) &&
  65375. sizeof($_PEAR_destructor_object_list))
  65376. {
  65377. reset($_PEAR_destructor_object_list);
  65378. $destructLifoExists = PEAR::getStaticProperty('PEAR', 'destructlifo');
  65379. if ($destructLifoExists) {
  65380. $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
  65381. }
  65382. foreach ($_PEAR_destructor_object_list as $k => $objref) {
  65383. $classname = get_class($objref);
  65384. while ($classname) {
  65385. $destructor = "_$classname";
  65386. if (method_exists($objref, $destructor)) {
  65387. $objref->$destructor();
  65388. break;
  65389. } else {
  65390. $classname = get_parent_class($classname);
  65391. }
  65392. }
  65393. }
  65394. // Empty the object list to ensure that destructors are
  65395. // not called more than once.
  65396. $_PEAR_destructor_object_list = array();
  65397. }
  65398. // Now call the shutdown functions
  65399. if (
  65400. isset($GLOBALS['_PEAR_shutdown_funcs']) &&
  65401. is_array($GLOBALS['_PEAR_shutdown_funcs']) &&
  65402. !empty($GLOBALS['_PEAR_shutdown_funcs'])
  65403. ) {
  65404. foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
  65405. call_user_func_array($value[0], $value[1]);
  65406. }
  65407. }
  65408. }
  65409. /**
  65410. * Standard PEAR error class for PHP 4
  65411. *
  65412. * This class is supserseded by {@link PEAR_Exception} in PHP 5
  65413. *
  65414. * @category pear
  65415. * @package PEAR
  65416. * @author Stig Bakken <ssb@php.net>
  65417. * @author Tomas V.V. Cox <cox@idecnet.com>
  65418. * @author Gregory Beaver <cellog@php.net>
  65419. * @copyright 1997-2006 The PHP Group
  65420. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  65421. * @version Release: 1.10.16
  65422. * @link http://pear.php.net/manual/en/core.pear.pear-error.php
  65423. * @see PEAR::raiseError(), PEAR::throwError()
  65424. * @since Class available since PHP 4.0.2
  65425. */
  65426. class PEAR_Error
  65427. {
  65428. var $error_message_prefix = '';
  65429. var $mode = PEAR_ERROR_RETURN;
  65430. var $level = E_USER_NOTICE;
  65431. var $code = -1;
  65432. var $message = '';
  65433. var $userinfo = '';
  65434. var $backtrace = null;
  65435. var $callback = null;
  65436. /**
  65437. * PEAR_Error constructor
  65438. *
  65439. * @param string $message message
  65440. *
  65441. * @param int $code (optional) error code
  65442. *
  65443. * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN,
  65444. * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
  65445. * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
  65446. *
  65447. * @param mixed $options (optional) error level, _OR_ in the case of
  65448. * PEAR_ERROR_CALLBACK, the callback function or object/method
  65449. * tuple.
  65450. *
  65451. * @param string $userinfo (optional) additional user/debug info
  65452. *
  65453. * @access public
  65454. *
  65455. */
  65456. function __construct($message = 'unknown error', $code = null,
  65457. $mode = null, $options = null, $userinfo = null)
  65458. {
  65459. if ($mode === null) {
  65460. $mode = PEAR_ERROR_RETURN;
  65461. }
  65462. $this->message = $message;
  65463. $this->code = $code;
  65464. $this->mode = $mode;
  65465. $this->userinfo = $userinfo;
  65466. $skiptrace = PEAR::getStaticProperty('PEAR_Error', 'skiptrace');
  65467. if (!$skiptrace) {
  65468. $this->backtrace = debug_backtrace();
  65469. if (isset($this->backtrace[0]) && isset($this->backtrace[0]['object'])) {
  65470. unset($this->backtrace[0]['object']);
  65471. }
  65472. }
  65473. if ($mode & PEAR_ERROR_CALLBACK) {
  65474. $this->level = E_USER_NOTICE;
  65475. $this->callback = $options;
  65476. } else {
  65477. if ($options === null) {
  65478. $options = E_USER_NOTICE;
  65479. }
  65480. $this->level = $options;
  65481. $this->callback = null;
  65482. }
  65483. if ($this->mode & PEAR_ERROR_PRINT) {
  65484. if (is_null($options) || is_int($options)) {
  65485. $format = "%s";
  65486. } else {
  65487. $format = $options;
  65488. }
  65489. printf($format, $this->getMessage());
  65490. }
  65491. if ($this->mode & PEAR_ERROR_TRIGGER) {
  65492. trigger_error($this->getMessage(), $this->level);
  65493. }
  65494. if ($this->mode & PEAR_ERROR_DIE) {
  65495. $msg = $this->getMessage();
  65496. if (is_null($options) || is_int($options)) {
  65497. $format = "%s";
  65498. if (substr($msg, -1) != "\n") {
  65499. $msg .= "\n";
  65500. }
  65501. } else {
  65502. $format = $options;
  65503. }
  65504. printf($format, $msg);
  65505. exit($code);
  65506. }
  65507. if ($this->mode & PEAR_ERROR_CALLBACK && is_callable($this->callback)) {
  65508. call_user_func($this->callback, $this);
  65509. }
  65510. if ($this->mode & PEAR_ERROR_EXCEPTION) {
  65511. trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_Exception for exceptions", E_USER_WARNING);
  65512. eval('$e = new Exception($this->message, $this->code);throw($e);');
  65513. }
  65514. }
  65515. /**
  65516. * Only here for backwards compatibility.
  65517. *
  65518. * Class "Cache_Error" still uses it, among others.
  65519. *
  65520. * @param string $message Message
  65521. * @param int $code Error code
  65522. * @param int $mode Error mode
  65523. * @param mixed $options See __construct()
  65524. * @param string $userinfo Additional user/debug info
  65525. */
  65526. public function PEAR_Error(
  65527. $message = 'unknown error', $code = null, $mode = null,
  65528. $options = null, $userinfo = null
  65529. ) {
  65530. self::__construct($message, $code, $mode, $options, $userinfo);
  65531. }
  65532. /**
  65533. * Get the error mode from an error object.
  65534. *
  65535. * @return int error mode
  65536. * @access public
  65537. */
  65538. function getMode()
  65539. {
  65540. return $this->mode;
  65541. }
  65542. /**
  65543. * Get the callback function/method from an error object.
  65544. *
  65545. * @return mixed callback function or object/method array
  65546. * @access public
  65547. */
  65548. function getCallback()
  65549. {
  65550. return $this->callback;
  65551. }
  65552. /**
  65553. * Get the error message from an error object.
  65554. *
  65555. * @return string full error message
  65556. * @access public
  65557. */
  65558. function getMessage()
  65559. {
  65560. return ($this->error_message_prefix . $this->message);
  65561. }
  65562. /**
  65563. * Get error code from an error object
  65564. *
  65565. * @return int error code
  65566. * @access public
  65567. */
  65568. function getCode()
  65569. {
  65570. return $this->code;
  65571. }
  65572. /**
  65573. * Get the name of this error/exception.
  65574. *
  65575. * @return string error/exception name (type)
  65576. * @access public
  65577. */
  65578. function getType()
  65579. {
  65580. return get_class($this);
  65581. }
  65582. /**
  65583. * Get additional user-supplied information.
  65584. *
  65585. * @return string user-supplied information
  65586. * @access public
  65587. */
  65588. function getUserInfo()
  65589. {
  65590. return $this->userinfo;
  65591. }
  65592. /**
  65593. * Get additional debug information supplied by the application.
  65594. *
  65595. * @return string debug information
  65596. * @access public
  65597. */
  65598. function getDebugInfo()
  65599. {
  65600. return $this->getUserInfo();
  65601. }
  65602. /**
  65603. * Get the call backtrace from where the error was generated.
  65604. * Supported with PHP 4.3.0 or newer.
  65605. *
  65606. * @param int $frame (optional) what frame to fetch
  65607. * @return array Backtrace, or NULL if not available.
  65608. * @access public
  65609. */
  65610. function getBacktrace($frame = null)
  65611. {
  65612. if (defined('PEAR_IGNORE_BACKTRACE')) {
  65613. return null;
  65614. }
  65615. if ($frame === null) {
  65616. return $this->backtrace;
  65617. }
  65618. return $this->backtrace[$frame];
  65619. }
  65620. function addUserInfo($info)
  65621. {
  65622. if (empty($this->userinfo)) {
  65623. $this->userinfo = $info;
  65624. } else {
  65625. $this->userinfo .= " ** $info";
  65626. }
  65627. }
  65628. function __toString()
  65629. {
  65630. return $this->getMessage();
  65631. }
  65632. /**
  65633. * Make a string representation of this object.
  65634. *
  65635. * @return string a string with an object summary
  65636. * @access public
  65637. */
  65638. function toString()
  65639. {
  65640. $modes = array();
  65641. $levels = array(E_USER_NOTICE => 'notice',
  65642. E_USER_WARNING => 'warning',
  65643. E_USER_ERROR => 'error');
  65644. if ($this->mode & PEAR_ERROR_CALLBACK) {
  65645. if (is_array($this->callback)) {
  65646. $callback = (is_object($this->callback[0]) ?
  65647. strtolower(get_class($this->callback[0])) :
  65648. $this->callback[0]) . '::' .
  65649. $this->callback[1];
  65650. } else {
  65651. $callback = $this->callback;
  65652. }
  65653. return sprintf('[%s: message="%s" code=%d mode=callback '.
  65654. 'callback=%s prefix="%s" info="%s"]',
  65655. strtolower(get_class($this)), $this->message, $this->code,
  65656. $callback, $this->error_message_prefix,
  65657. $this->userinfo);
  65658. }
  65659. if ($this->mode & PEAR_ERROR_PRINT) {
  65660. $modes[] = 'print';
  65661. }
  65662. if ($this->mode & PEAR_ERROR_TRIGGER) {
  65663. $modes[] = 'trigger';
  65664. }
  65665. if ($this->mode & PEAR_ERROR_DIE) {
  65666. $modes[] = 'die';
  65667. }
  65668. if ($this->mode & PEAR_ERROR_RETURN) {
  65669. $modes[] = 'return';
  65670. }
  65671. return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
  65672. 'prefix="%s" info="%s"]',
  65673. strtolower(get_class($this)), $this->message, $this->code,
  65674. implode("|", $modes), $levels[$this->level],
  65675. $this->error_message_prefix,
  65676. $this->userinfo);
  65677. }
  65678. }
  65679. /*
  65680. * Local Variables:
  65681. * mode: php
  65682. * tab-width: 4
  65683. * c-basic-offset: 4
  65684. * End:
  65685. */
  65686. ������������������������������������������������������������������������������������������������������������PEAR-1.10.16/README.rst�����������������������������������������������������������������������������0000664�0001750�0001750�00000004342�14720722517�013762� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������*************************
  65687. PEAR - The PEAR Installer
  65688. *************************
  65689. .. image:: https://travis-ci.org/pear/pear-core.svg?branch=stable
  65690. :target: https://travis-ci.org/pear/pear-core
  65691. =========================================
  65692. What is the PEAR Installer? What is PEAR?
  65693. =========================================
  65694. PEAR is the PHP Extension and Application Repository, found at
  65695. http://pear.php.net.
  65696. The **PEAR Installer** is this software, which contains executable
  65697. files and PHP code that is used to **download and install** PEAR code
  65698. from pear.php.net.
  65699. PEAR contains useful **software libraries and applications** such as
  65700. MDB2 (database abstraction), HTML_QuickForm (HTML forms management),
  65701. PhpDocumentor (auto-documentation generator), DB_DataObject
  65702. (Data Access Abstraction), and many hundreds more.
  65703. Browse all available packages at http://pear.php.net, the list is
  65704. constantly growing and updating to reflect improvements in the PHP language.
  65705. .. warning::
  65706. Do not run PEAR without installing it - if you downloaded this
  65707. tarball manually, you MUST install it. Read the instructions in INSTALL
  65708. prior to use.
  65709. =============
  65710. Documentation
  65711. =============
  65712. Documentation for PEAR can be found at http://pear.php.net/manual/.
  65713. Installation documentation can be found in the INSTALL file included
  65714. in this tarball.
  65715. =====
  65716. Tests
  65717. =====
  65718. Run the tests without installation as follows::
  65719. $ ./scripts/pear.sh run-tests -r tests
  65720. You should have the ``Text_Diff`` package installed to get nicer error output.
  65721. To run the tests with another PHP version, modify ``php_bin`` and set the
  65722. ``PHP_PEAR_PHP_BIN`` environment variable::
  65723. $ pear config-set php_bin /usr/local/bin/php7
  65724. $ PHP_PEAR_PHP_BIN=/usr/local/bin/php7 ./scripts/pear.sh run-tests -r tests
  65725. Happy PHPing, we hope PEAR will be a great tool for your development work!
  65726. Test dependencies
  65727. =================
  65728. * ``zlib``
  65729. =========
  65730. Releasing
  65731. =========
  65732. Create a PEAR package, as well as phars for pear-less installation,
  65733. simply run ``build-release.sh``).
  65734. ``go-pear.phar`` contains the PEAR installer installer that asks where to install it.
  65735. It is available from http://pear.php.net/go-pear.phar.
  65736. ``install-pear-nozlib.phar`` installs PEAR automatically without asking anything.
  65737. It is shipped with PHP itself.
  65738. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/System.php�����������������������������������������������������������������������������0000664�0001750�0001750�00000050326�14720722517�014273� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  65739. /**
  65740. * File/Directory manipulation
  65741. *
  65742. * PHP versions 4 and 5
  65743. *
  65744. * @category pear
  65745. * @package System
  65746. * @author Tomas V.V.Cox <cox@idecnet.com>
  65747. * @copyright 1997-2009 The Authors
  65748. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  65749. * @link http://pear.php.net/package/PEAR
  65750. * @since File available since Release 0.1
  65751. */
  65752. /**
  65753. * base class
  65754. */
  65755. require_once 'PEAR.php';
  65756. require_once 'Console/Getopt.php';
  65757. $GLOBALS['_System_temp_files'] = array();
  65758. /**
  65759. * System offers cross platform compatible system functions
  65760. *
  65761. * Static functions for different operations. Should work under
  65762. * Unix and Windows. The names and usage has been taken from its respectively
  65763. * GNU commands. The functions will return (bool) false on error and will
  65764. * trigger the error with the PHP trigger_error() function (you can silence
  65765. * the error by prefixing a '@' sign after the function call, but this
  65766. * is not recommended practice. Instead use an error handler with
  65767. * {@link set_error_handler()}).
  65768. *
  65769. * Documentation on this class you can find in:
  65770. * http://pear.php.net/manual/
  65771. *
  65772. * Example usage:
  65773. * if (!@System::rm('-r file1 dir1')) {
  65774. * print "could not delete file1 or dir1";
  65775. * }
  65776. *
  65777. * In case you need to to pass file names with spaces,
  65778. * pass the params as an array:
  65779. *
  65780. * System::rm(array('-r', $file1, $dir1));
  65781. *
  65782. * @category pear
  65783. * @package System
  65784. * @author Tomas V.V. Cox <cox@idecnet.com>
  65785. * @copyright 1997-2006 The PHP Group
  65786. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  65787. * @version Release: 1.10.16
  65788. * @link http://pear.php.net/package/PEAR
  65789. * @since Class available since Release 0.1
  65790. * @static
  65791. */
  65792. class System
  65793. {
  65794. /**
  65795. * returns the commandline arguments of a function
  65796. *
  65797. * @param string $argv the commandline
  65798. * @param string $short_options the allowed option short-tags
  65799. * @param string $long_options the allowed option long-tags
  65800. * @return array the given options and there values
  65801. */
  65802. public static function _parseArgs($argv, $short_options, $long_options = null)
  65803. {
  65804. if (!is_array($argv) && $argv !== null) {
  65805. /*
  65806. // Quote all items that are a short option
  65807. $av = preg_split('/(\A| )--?[a-z0-9]+[ =]?((?<!\\\\)((,\s*)|((?<!,)\s+))?)/i', $argv, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE);
  65808. $offset = 0;
  65809. foreach ($av as $a) {
  65810. $b = trim($a[0]);
  65811. if ($b[0] == '"' || $b[0] == "'") {
  65812. continue;
  65813. }
  65814. $escape = escapeshellarg($b);
  65815. $pos = $a[1] + $offset;
  65816. $argv = substr_replace($argv, $escape, $pos, strlen($b));
  65817. $offset += 2;
  65818. }
  65819. */
  65820. // Find all items, quoted or otherwise
  65821. preg_match_all("/(?:[\"'])(.*?)(?:['\"])|([^\s]+)/", $argv, $av);
  65822. $argv = $av[1];
  65823. foreach ($av[2] as $k => $a) {
  65824. if (empty($a)) {
  65825. continue;
  65826. }
  65827. $argv[$k] = trim($a) ;
  65828. }
  65829. }
  65830. return Console_Getopt::getopt2($argv, $short_options, $long_options);
  65831. }
  65832. /**
  65833. * Output errors with PHP trigger_error(). You can silence the errors
  65834. * with prefixing a "@" sign to the function call: @System::mkdir(..);
  65835. *
  65836. * @param mixed $error a PEAR error or a string with the error message
  65837. * @return bool false
  65838. */
  65839. protected static function raiseError($error)
  65840. {
  65841. if (PEAR::isError($error)) {
  65842. $error = $error->getMessage();
  65843. }
  65844. trigger_error($error, E_USER_WARNING);
  65845. return false;
  65846. }
  65847. /**
  65848. * Creates a nested array representing the structure of a directory
  65849. *
  65850. * System::_dirToStruct('dir1', 0) =>
  65851. * Array
  65852. * (
  65853. * [dirs] => Array
  65854. * (
  65855. * [0] => dir1
  65856. * )
  65857. *
  65858. * [files] => Array
  65859. * (
  65860. * [0] => dir1/file2
  65861. * [1] => dir1/file3
  65862. * )
  65863. * )
  65864. * @param string $sPath Name of the directory
  65865. * @param integer $maxinst max. deep of the lookup
  65866. * @param integer $aktinst starting deep of the lookup
  65867. * @param bool $silent if true, do not emit errors.
  65868. * @return array the structure of the dir
  65869. */
  65870. protected static function _dirToStruct($sPath, $maxinst, $aktinst = 0, $silent = false)
  65871. {
  65872. $struct = array('dirs' => array(), 'files' => array());
  65873. if (($dir = @opendir($sPath)) === false) {
  65874. if (!$silent) {
  65875. System::raiseError("Could not open dir $sPath");
  65876. }
  65877. return $struct; // XXX could not open error
  65878. }
  65879. $struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ?
  65880. $list = array();
  65881. while (false !== ($file = readdir($dir))) {
  65882. if ($file != '.' && $file != '..') {
  65883. $list[] = $file;
  65884. }
  65885. }
  65886. closedir($dir);
  65887. natsort($list);
  65888. if ($aktinst < $maxinst || $maxinst == 0) {
  65889. foreach ($list as $val) {
  65890. $path = $sPath . DIRECTORY_SEPARATOR . $val;
  65891. if (is_dir($path) && !is_link($path)) {
  65892. $tmp = System::_dirToStruct($path, $maxinst, $aktinst+1, $silent);
  65893. $struct = array_merge_recursive($struct, $tmp);
  65894. } else {
  65895. $struct['files'][] = $path;
  65896. }
  65897. }
  65898. }
  65899. return $struct;
  65900. }
  65901. /**
  65902. * Creates a nested array representing the structure of a directory and files
  65903. *
  65904. * @param array $files Array listing files and dirs
  65905. * @return array
  65906. * @static
  65907. * @see System::_dirToStruct()
  65908. */
  65909. protected static function _multipleToStruct($files)
  65910. {
  65911. $struct = array('dirs' => array(), 'files' => array());
  65912. settype($files, 'array');
  65913. foreach ($files as $file) {
  65914. if (is_dir($file) && !is_link($file)) {
  65915. $tmp = System::_dirToStruct($file, 0);
  65916. $struct = array_merge_recursive($tmp, $struct);
  65917. } else {
  65918. if (!in_array($file, $struct['files'])) {
  65919. $struct['files'][] = $file;
  65920. }
  65921. }
  65922. }
  65923. return $struct;
  65924. }
  65925. /**
  65926. * The rm command for removing files.
  65927. * Supports multiple files and dirs and also recursive deletes
  65928. *
  65929. * @param string $args the arguments for rm
  65930. * @return mixed PEAR_Error or true for success
  65931. * @static
  65932. * @access public
  65933. */
  65934. public static function rm($args)
  65935. {
  65936. $opts = System::_parseArgs($args, 'rf'); // "f" does nothing but I like it :-)
  65937. if (PEAR::isError($opts)) {
  65938. return System::raiseError($opts);
  65939. }
  65940. foreach ($opts[0] as $opt) {
  65941. if ($opt[0] == 'r') {
  65942. $do_recursive = true;
  65943. }
  65944. }
  65945. $ret = true;
  65946. if (isset($do_recursive)) {
  65947. $struct = System::_multipleToStruct($opts[1]);
  65948. foreach ($struct['files'] as $file) {
  65949. if (!@unlink($file)) {
  65950. $ret = false;
  65951. }
  65952. }
  65953. rsort($struct['dirs']);
  65954. foreach ($struct['dirs'] as $dir) {
  65955. if (!@rmdir($dir)) {
  65956. $ret = false;
  65957. }
  65958. }
  65959. } else {
  65960. foreach ($opts[1] as $file) {
  65961. $delete = (is_dir($file)) ? 'rmdir' : 'unlink';
  65962. if (!@$delete($file)) {
  65963. $ret = false;
  65964. }
  65965. }
  65966. }
  65967. return $ret;
  65968. }
  65969. /**
  65970. * Make directories.
  65971. *
  65972. * The -p option will create parent directories
  65973. * @param string $args the name of the director(y|ies) to create
  65974. * @return bool True for success
  65975. */
  65976. public static function mkDir($args)
  65977. {
  65978. $opts = System::_parseArgs($args, 'pm:');
  65979. if (PEAR::isError($opts)) {
  65980. return System::raiseError($opts);
  65981. }
  65982. $mode = 0777; // default mode
  65983. foreach ($opts[0] as $opt) {
  65984. if ($opt[0] == 'p') {
  65985. $create_parents = true;
  65986. } elseif ($opt[0] == 'm') {
  65987. // if the mode is clearly an octal number (starts with 0)
  65988. // convert it to decimal
  65989. if (strlen($opt[1]) && $opt[1][0] == '0') {
  65990. $opt[1] = octdec($opt[1]);
  65991. } else {
  65992. // convert to int
  65993. $opt[1] += 0;
  65994. }
  65995. $mode = $opt[1];
  65996. }
  65997. }
  65998. $ret = true;
  65999. if (isset($create_parents)) {
  66000. foreach ($opts[1] as $dir) {
  66001. $dirstack = array();
  66002. while ((!file_exists($dir) || !is_dir($dir)) &&
  66003. $dir != DIRECTORY_SEPARATOR) {
  66004. array_unshift($dirstack, $dir);
  66005. $dir = dirname($dir);
  66006. }
  66007. while ($newdir = array_shift($dirstack)) {
  66008. if (!is_writeable(dirname($newdir))) {
  66009. $ret = false;
  66010. break;
  66011. }
  66012. if (!mkdir($newdir, $mode)) {
  66013. $ret = false;
  66014. }
  66015. }
  66016. }
  66017. } else {
  66018. foreach($opts[1] as $dir) {
  66019. if ((@file_exists($dir) || !is_dir($dir)) && !mkdir($dir, $mode)) {
  66020. $ret = false;
  66021. }
  66022. }
  66023. }
  66024. return $ret;
  66025. }
  66026. /**
  66027. * Concatenate files
  66028. *
  66029. * Usage:
  66030. * 1) $var = System::cat('sample.txt test.txt');
  66031. * 2) System::cat('sample.txt test.txt > final.txt');
  66032. * 3) System::cat('sample.txt test.txt >> final.txt');
  66033. *
  66034. * Note: as the class use fopen, urls should work also
  66035. *
  66036. * @param string $args the arguments
  66037. * @return boolean true on success
  66038. */
  66039. public static function &cat($args)
  66040. {
  66041. $ret = null;
  66042. $files = array();
  66043. if (!is_array($args)) {
  66044. $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
  66045. }
  66046. $count_args = count($args);
  66047. for ($i = 0; $i < $count_args; $i++) {
  66048. if ($args[$i] == '>') {
  66049. $mode = 'wb';
  66050. $outputfile = $args[$i+1];
  66051. break;
  66052. } elseif ($args[$i] == '>>') {
  66053. $mode = 'ab+';
  66054. $outputfile = $args[$i+1];
  66055. break;
  66056. } else {
  66057. $files[] = $args[$i];
  66058. }
  66059. }
  66060. $outputfd = false;
  66061. if (isset($mode)) {
  66062. if (!$outputfd = fopen($outputfile, $mode)) {
  66063. $err = System::raiseError("Could not open $outputfile");
  66064. return $err;
  66065. }
  66066. $ret = true;
  66067. }
  66068. foreach ($files as $file) {
  66069. if (!$fd = fopen($file, 'r')) {
  66070. System::raiseError("Could not open $file");
  66071. continue;
  66072. }
  66073. while ($cont = fread($fd, 2048)) {
  66074. if (is_resource($outputfd)) {
  66075. fwrite($outputfd, $cont);
  66076. } else {
  66077. $ret .= $cont;
  66078. }
  66079. }
  66080. fclose($fd);
  66081. }
  66082. if (is_resource($outputfd)) {
  66083. fclose($outputfd);
  66084. }
  66085. return $ret;
  66086. }
  66087. /**
  66088. * Creates temporary files or directories. This function will remove
  66089. * the created files when the scripts finish its execution.
  66090. *
  66091. * Usage:
  66092. * 1) $tempfile = System::mktemp("prefix");
  66093. * 2) $tempdir = System::mktemp("-d prefix");
  66094. * 3) $tempfile = System::mktemp();
  66095. * 4) $tempfile = System::mktemp("-t /var/tmp prefix");
  66096. *
  66097. * prefix -> The string that will be prepended to the temp name
  66098. * (defaults to "tmp").
  66099. * -d -> A temporary dir will be created instead of a file.
  66100. * -t -> The target dir where the temporary (file|dir) will be created. If
  66101. * this param is missing by default the env vars TMP on Windows or
  66102. * TMPDIR in Unix will be used. If these vars are also missing
  66103. * c:\windows\temp or /tmp will be used.
  66104. *
  66105. * @param string $args The arguments
  66106. * @return mixed the full path of the created (file|dir) or false
  66107. * @see System::tmpdir()
  66108. */
  66109. public static function mktemp($args = null)
  66110. {
  66111. static $first_time = true;
  66112. $opts = System::_parseArgs($args, 't:d');
  66113. if (PEAR::isError($opts)) {
  66114. return System::raiseError($opts);
  66115. }
  66116. foreach ($opts[0] as $opt) {
  66117. if ($opt[0] == 'd') {
  66118. $tmp_is_dir = true;
  66119. } elseif ($opt[0] == 't') {
  66120. $tmpdir = $opt[1];
  66121. }
  66122. }
  66123. $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp';
  66124. if (!isset($tmpdir)) {
  66125. $tmpdir = System::tmpdir();
  66126. }
  66127. if (!System::mkDir(array('-p', $tmpdir))) {
  66128. return false;
  66129. }
  66130. $tmp = tempnam($tmpdir, $prefix);
  66131. if (isset($tmp_is_dir)) {
  66132. unlink($tmp); // be careful possible race condition here
  66133. if (!mkdir($tmp, 0700)) {
  66134. return System::raiseError("Unable to create temporary directory $tmpdir");
  66135. }
  66136. }
  66137. $GLOBALS['_System_temp_files'][] = $tmp;
  66138. if (isset($tmp_is_dir)) {
  66139. //$GLOBALS['_System_temp_files'][] = dirname($tmp);
  66140. }
  66141. if ($first_time) {
  66142. PEAR::registerShutdownFunc(array('System', '_removeTmpFiles'));
  66143. $first_time = false;
  66144. }
  66145. return $tmp;
  66146. }
  66147. /**
  66148. * Remove temporary files created my mkTemp. This function is executed
  66149. * at script shutdown time
  66150. */
  66151. public static function _removeTmpFiles()
  66152. {
  66153. if (count($GLOBALS['_System_temp_files'])) {
  66154. $delete = $GLOBALS['_System_temp_files'];
  66155. array_unshift($delete, '-r');
  66156. System::rm($delete);
  66157. $GLOBALS['_System_temp_files'] = array();
  66158. }
  66159. }
  66160. /**
  66161. * Get the path of the temporal directory set in the system
  66162. * by looking in its environments variables.
  66163. * Note: php.ini-recommended removes the "E" from the variables_order setting,
  66164. * making unavaible the $_ENV array, that s why we do tests with _ENV
  66165. *
  66166. * @return string The temporary directory on the system
  66167. */
  66168. public static function tmpdir()
  66169. {
  66170. if (OS_WINDOWS) {
  66171. if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) {
  66172. return $var;
  66173. }
  66174. if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) {
  66175. return $var;
  66176. }
  66177. if ($var = isset($_ENV['USERPROFILE']) ? $_ENV['USERPROFILE'] : getenv('USERPROFILE')) {
  66178. return $var;
  66179. }
  66180. if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) {
  66181. return $var;
  66182. }
  66183. return getenv('SystemRoot') . '\temp';
  66184. }
  66185. if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) {
  66186. return $var;
  66187. }
  66188. return realpath(function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : '/tmp');
  66189. }
  66190. /**
  66191. * The "which" command (show the full path of a command)
  66192. *
  66193. * @param string $program The command to search for
  66194. * @param mixed $fallback Value to return if $program is not found
  66195. *
  66196. * @return mixed A string with the full path or false if not found
  66197. * @author Stig Bakken <ssb@php.net>
  66198. */
  66199. public static function which($program, $fallback = false)
  66200. {
  66201. // enforce API
  66202. if (!is_string($program) || '' == $program) {
  66203. return $fallback;
  66204. }
  66205. // full path given
  66206. if (basename($program) != $program) {
  66207. $path_elements[] = dirname($program);
  66208. $program = basename($program);
  66209. } else {
  66210. $path = getenv('PATH');
  66211. if (!$path) {
  66212. $path = getenv('Path'); // some OSes are just stupid enough to do this
  66213. }
  66214. $path_elements = explode(PATH_SEPARATOR, $path);
  66215. }
  66216. if (OS_WINDOWS) {
  66217. $exe_suffixes = getenv('PATHEXT')
  66218. ? explode(PATH_SEPARATOR, getenv('PATHEXT'))
  66219. : array('.exe','.bat','.cmd','.com');
  66220. // allow passing a command.exe param
  66221. if (strpos($program, '.') !== false) {
  66222. array_unshift($exe_suffixes, '');
  66223. }
  66224. } else {
  66225. $exe_suffixes = array('');
  66226. }
  66227. foreach ($exe_suffixes as $suff) {
  66228. foreach ($path_elements as $dir) {
  66229. $file = $dir . DIRECTORY_SEPARATOR . $program . $suff;
  66230. // It's possible to run a .bat on Windows that is_executable
  66231. // would return false for. The is_executable check is meaningless...
  66232. if (OS_WINDOWS) {
  66233. if (file_exists($file)) {
  66234. return $file;
  66235. }
  66236. } else {
  66237. if (is_executable($file)) {
  66238. return $file;
  66239. }
  66240. }
  66241. }
  66242. }
  66243. return $fallback;
  66244. }
  66245. /**
  66246. * The "find" command
  66247. *
  66248. * Usage:
  66249. *
  66250. * System::find($dir);
  66251. * System::find("$dir -type d");
  66252. * System::find("$dir -type f");
  66253. * System::find("$dir -name *.php");
  66254. * System::find("$dir -name *.php -name *.htm*");
  66255. * System::find("$dir -maxdepth 1");
  66256. *
  66257. * Params implemented:
  66258. * $dir -> Start the search at this directory
  66259. * -type d -> return only directories
  66260. * -type f -> return only files
  66261. * -maxdepth <n> -> max depth of recursion
  66262. * -name <pattern> -> search pattern (bash style). Multiple -name param allowed
  66263. *
  66264. * @param mixed Either array or string with the command line
  66265. * @return array Array of found files
  66266. */
  66267. public static function find($args)
  66268. {
  66269. if (!is_array($args)) {
  66270. $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
  66271. }
  66272. $dir = realpath(array_shift($args));
  66273. if (!$dir) {
  66274. return array();
  66275. }
  66276. $patterns = array();
  66277. $depth = 0;
  66278. $do_files = $do_dirs = true;
  66279. $args_count = count($args);
  66280. for ($i = 0; $i < $args_count; $i++) {
  66281. switch ($args[$i]) {
  66282. case '-type':
  66283. if (in_array($args[$i+1], array('d', 'f'))) {
  66284. if ($args[$i+1] == 'd') {
  66285. $do_files = false;
  66286. } else {
  66287. $do_dirs = false;
  66288. }
  66289. }
  66290. $i++;
  66291. break;
  66292. case '-name':
  66293. $name = preg_quote($args[$i+1], '#');
  66294. // our magic characters ? and * have just been escaped,
  66295. // so now we change the escaped versions to PCRE operators
  66296. $name = strtr($name, array('\?' => '.', '\*' => '.*'));
  66297. $patterns[] = '('.$name.')';
  66298. $i++;
  66299. break;
  66300. case '-maxdepth':
  66301. $depth = $args[$i+1];
  66302. break;
  66303. }
  66304. }
  66305. $path = System::_dirToStruct($dir, $depth, 0, true);
  66306. if ($do_files && $do_dirs) {
  66307. $files = array_merge($path['files'], $path['dirs']);
  66308. } elseif ($do_dirs) {
  66309. $files = $path['dirs'];
  66310. } else {
  66311. $files = $path['files'];
  66312. }
  66313. if (count($patterns)) {
  66314. $dsq = preg_quote(DIRECTORY_SEPARATOR, '#');
  66315. $pattern = '#(^|'.$dsq.')'.implode('|', $patterns).'($|'.$dsq.')#';
  66316. $ret = array();
  66317. $files_count = count($files);
  66318. for ($i = 0; $i < $files_count; $i++) {
  66319. // only search in the part of the file below the current directory
  66320. $filepart = basename($files[$i]);
  66321. if (preg_match($pattern, $filepart)) {
  66322. $ret[] = $files[$i];
  66323. }
  66324. }
  66325. return $ret;
  66326. }
  66327. return $files;
  66328. }
  66329. }
  66330. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������PEAR-1.10.16/template.spec��������������������������������������������������������������������������0000644�0001750�0001750�00000003725�14720722517�014764� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Summary: PEAR: @summary@
  66331. Name: @rpm_package@
  66332. Version: @version@
  66333. Release: 1
  66334. License: @release_license@
  66335. Group: Development/Libraries
  66336. Source: http://@master_server@/get/@package@-%{version}.tgz
  66337. BuildRoot: %{_tmppath}/%{name}-root
  66338. URL: http://@master_server@/package/@package@
  66339. Prefix: %{_prefix}
  66340. BuildArchitectures: @arch@
  66341. @extra_headers@
  66342. %description
  66343. @description@
  66344. %prep
  66345. rm -rf %{buildroot}/*
  66346. %setup -c -T
  66347. # XXX Source files location is missing here in pear cmd
  66348. pear -v -c %{buildroot}/pearrc \
  66349. -d php_dir=%{_libdir}/php/pear \
  66350. -d doc_dir=/docs \
  66351. -d bin_dir=%{_bindir} \
  66352. -d data_dir=%{_libdir}/php/pear/data \
  66353. -d test_dir=%{_libdir}/php/pear/tests \
  66354. -d ext_dir=%{_libdir} \@extra_config@
  66355. -s
  66356. %build
  66357. echo BuildRoot=%{buildroot}
  66358. %postun
  66359. # if refcount = 0 then package has been removed (not upgraded)
  66360. if [ "$1" -eq "0" ]; then
  66361. pear uninstall --nodeps -r @possible_channel@@package@
  66362. rm @rpm_xml_dir@/@package@.xml
  66363. fi
  66364. %post
  66365. # if refcount = 2 then package has been upgraded
  66366. if [ "$1" -ge "2" ]; then
  66367. pear upgrade --nodeps -r @rpm_xml_dir@/@package@.xml
  66368. else
  66369. pear install --nodeps -r @rpm_xml_dir@/@package@.xml
  66370. fi
  66371. %install
  66372. pear -c %{buildroot}/pearrc install --nodeps -R %{buildroot} \
  66373. $RPM_SOURCE_DIR/@package@-%{version}.tgz
  66374. rm %{buildroot}/pearrc
  66375. rm %{buildroot}/%{_libdir}/php/pear/.filemap
  66376. rm %{buildroot}/%{_libdir}/php/pear/.lock
  66377. rm -rf %{buildroot}/%{_libdir}/php/pear/.registry
  66378. if [ "@doc_files@" != "" ]; then
  66379. mv %{buildroot}/docs/@package@/* .
  66380. rm -rf %{buildroot}/docs
  66381. fi
  66382. mkdir -p %{buildroot}@rpm_xml_dir@
  66383. tar -xzf $RPM_SOURCE_DIR/@package@-%{version}.tgz package@package2xml@.xml
  66384. cp -p package@package2xml@.xml %{buildroot}@rpm_xml_dir@/@package@.xml
  66385. #rm -rf %{buildroot}/*
  66386. #pear -q install -R %{buildroot} -n package@package2xml@.xml
  66387. #mkdir -p %{buildroot}@rpm_xml_dir@
  66388. #cp -p package@package2xml@.xml %{buildroot}@rpm_xml_dir@/@package@.xml
  66389. %files
  66390. %defattr(-,root,root)
  66391. %doc @doc_files@
  66392. /
  66393. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package.xml�����������������������������������������������������������������������������������������0000664�0001750�0001750�00000013142�14627105564�013060� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?>
  66394. <package packagerversion="1.10.14" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
  66395. <name>Structures_Graph</name>
  66396. <channel>pear.php.net</channel>
  66397. <summary>Graph datastructure manipulation library</summary>
  66398. <description>Structures_Graph is a package for creating and manipulating graph datastructures. It allows building of directed
  66399. and undirected graphs, with data and metadata stored in nodes. The library provides functions for graph traversing
  66400. as well as for characteristic extraction from the graph topology.</description>
  66401. <lead>
  66402. <name>Sérgio Carvalho</name>
  66403. <user>sergiosgc</user>
  66404. <email>sergio.carvalho@portugalmail.com</email>
  66405. <active>yes</active>
  66406. </lead>
  66407. <helper>
  66408. <name>Brett Bieber</name>
  66409. <user>saltybeagle</user>
  66410. <email>brett.bieber@gmail.com</email>
  66411. <active>yes</active>
  66412. </helper>
  66413. <date>2024-06-02</date>
  66414. <time>15:10:44</time>
  66415. <version>
  66416. <release>1.2.0</release>
  66417. <api>1.2.0</api>
  66418. </version>
  66419. <stability>
  66420. <release>stable</release>
  66421. <api>stable</api>
  66422. </stability>
  66423. <license>LGPL-3.0+</license>
  66424. <notes>
  66425. * update composer info (adds PHP/PEAR deps)
  66426. * update phpunit tests
  66427. * composer shows wrong license
  66428. </notes>
  66429. <contents>
  66430. <dir baseinstalldir="/" name="/">
  66431. <file baseinstalldir="/" md5sum="628eb6532a8047bf5962fe24c1c245df" name="docs/tutorials/Structures_Graph/Structures_Graph.pkg" role="doc" />
  66432. <file baseinstalldir="/" md5sum="4b26eecd30f8695fc3739b1a5b59518e" name="Structures/Graph/Manipulator/AcyclicTest.php" role="php" />
  66433. <file baseinstalldir="/" md5sum="1f857de1fbbaace54b857ed9712f399f" name="Structures/Graph/Manipulator/TopologicalSorter.php" role="php" />
  66434. <file baseinstalldir="/" md5sum="f8e969f0b45d3859408901c8350bb701" name="Structures/Graph/Node.php" role="php" />
  66435. <file baseinstalldir="/" md5sum="88ae1ad8bcd74d4b74ad845f55611cdd" name="Structures/Graph.php" role="php" />
  66436. <file baseinstalldir="/" md5sum="bfc441ea8614afed0f1e9074616a429b" name="tests/BasicGraphTest.php" role="test" />
  66437. <file baseinstalldir="/" md5sum="8192d7b593d807191bf4aa9207bfbab5" name="tests/TopologicalSorterTest.php" role="test" />
  66438. <file baseinstalldir="/" md5sum="8003f415d040b250e9e9c22c76b1cb59" name="tests/AcyclicTestTest.php" role="test" />
  66439. <file baseinstalldir="/" md5sum="bfc441ea8614afed0f1e9074616a429b" name="tests/BasicGraphTest.php" role="test" />
  66440. <file baseinstalldir="/" md5sum="c891580ee21a7aa863ac32566c979fc5" name="tests/helper.inc" role="test">
  66441. <tasks:replace from="@php_dir@" to="php_dir" type="pear-config" />
  66442. </file>
  66443. <file baseinstalldir="/" md5sum="b52f2d57d10c4f7ee67a7eb9615d5d24" name="LICENSE" role="doc" />
  66444. </dir>
  66445. </contents>
  66446. <compatible>
  66447. <name>PEAR</name>
  66448. <channel>pear.php.net</channel>
  66449. <min>1.5.0RC3</min>
  66450. <max>1.9.1</max>
  66451. </compatible>
  66452. <dependencies>
  66453. <required>
  66454. <php>
  66455. <min>5.3.0</min>
  66456. </php>
  66457. <pearinstaller>
  66458. <min>1.4.3</min>
  66459. </pearinstaller>
  66460. </required>
  66461. </dependencies>
  66462. <phprelease />
  66463. <changelog>
  66464. <release>
  66465. <version>
  66466. <release>1.0.2</release>
  66467. <api>1.0.0</api>
  66468. </version>
  66469. <stability>
  66470. <release>stable</release>
  66471. <api>stable</api>
  66472. </stability>
  66473. <date>2007-01-07</date>
  66474. <license uri="http://opensource.org/licenses/lgpl-license.php">LGPL</license>
  66475. <notes>
  66476. - Bug #9682 only variables can be returned by reference
  66477. - fix Bug #9661 notice in Structures_Graph_Manipulator_Topological::sort()
  66478. </notes>
  66479. </release>
  66480. <release>
  66481. <version>
  66482. <release>1.0.3</release>
  66483. <api>1.0.3</api>
  66484. </version>
  66485. <stability>
  66486. <release>stable</release>
  66487. <api>stable</api>
  66488. </stability>
  66489. <date>2009-10-11</date>
  66490. <license>LGPL License</license>
  66491. <notes>
  66492. Bugfix Release:
  66493. Version 1.0.3 is functionally equivalent to 1.0.2 but with an updated package.xml file.
  66494. * Correct invalid md5 sum preventing installation with pyrus [saltybeagle]
  66495. * Add compatible tag for PEAR 1.5.0RC3-1.9.0 [saltybeagle]
  66496. * Update package.xml
  66497. </notes>
  66498. </release>
  66499. <release>
  66500. <version>
  66501. <release>1.0.4</release>
  66502. <api>1.0.3</api>
  66503. </version>
  66504. <stability>
  66505. <release>stable</release>
  66506. <api>stable</api>
  66507. </stability>
  66508. <date>2010-10-25</date>
  66509. <license>LGPL License</license>
  66510. <notes>
  66511. Bugfix Release:
  66512. * Bug #17108 BasicGraph::test_directed_degree fails on PHP 5 [clockwerx]
  66513. </notes>
  66514. </release>
  66515. <release>
  66516. <version>
  66517. <release>1.1.0</release>
  66518. <api>1.1.0</api>
  66519. </version>
  66520. <stability>
  66521. <release>stable</release>
  66522. <api>stable</api>
  66523. </stability>
  66524. <date>2015-02-26</date>
  66525. <license>LGPL-3.0+</license>
  66526. <notes>
  66527. * Set minimum PHP version to 5.3
  66528. * Fix bug #19367: Incorrect FSF address in LICENSE
  66529. * Change license from LGPL-2.1+ to LGPL-3.0+
  66530. </notes>
  66531. </release>
  66532. <release>
  66533. <version>
  66534. <release>1.1.1</release>
  66535. <api>1.1.0</api>
  66536. </version>
  66537. <stability>
  66538. <release>stable</release>
  66539. <api>stable</api>
  66540. </stability>
  66541. <date>2015-07-20</date>
  66542. <license>LGPL-3.0+</license>
  66543. <notes>
  66544. * Fix deprecated constructor warning on PHP 7 [cweiske]
  66545. </notes>
  66546. </release>
  66547. <release>
  66548. <version>
  66549. <release>1.2.0</release>
  66550. <api>1.2.0</api>
  66551. </version>
  66552. <stability>
  66553. <release>stable</release>
  66554. <api>stable</api>
  66555. </stability>
  66556. <date>2024-06-02</date>
  66557. <license>LGPL-3.0+</license>
  66558. <notes>
  66559. * update composer info (adds PHP/PEAR deps)
  66560. * update phpunit tests
  66561. * composer shows wrong license
  66562. </notes>
  66563. </release>
  66564. </changelog>
  66565. </package>
  66566. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Structures_Graph-1.2.0/docs/tutorials/Structures_Graph/Structures_Graph.pkg�������������������������0000644�0001750�0001750�00000007714�14627105564�027202� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<refentry id="{@id package.database.structures_graph.tutorial}">
  66567. <refnamediv>
  66568. <refname><classname>Structures_Graph</classname> Tutorial</refname>
  66569. <refpurpose>A first tour of graph datastructure manipulation</refpurpose>
  66570. </refnamediv>
  66571. <refsect1 id="{@id package.database.structures_graph.tutorial.intro}">
  66572. <title>Introduction</title>
  66573. <para>
  66574. Structures_Graph is a package for creating and manipulating graph datastructures. A graph is a set of objects, called nodes, connected by arcs. When used as a datastructure, usually nodes contain data, and arcs represent relationships between nodes. When arcs have a direction, and can be travelled only one way, graphs are said to be directed. When arcs have no direction, and can always be travelled both ways, graphs are said to be non directed.
  66575. </para>
  66576. <para>
  66577. Structures_Graph provides an object oriented API to create and directly query a graph, as well as a set of Manipulator classes to extract information from the graph.
  66578. </para>
  66579. </refsect1>
  66580. <refsect1 id="{@id package.database.structures_graph.tutorial.creation}">
  66581. <title>Creating a Graph</title>
  66582. <para>
  66583. Creating a graph is done using the simple constructor:
  66584. <programlisting>
  66585. <![CDATA[
  66586. require_once 'Structures/Graph.php';
  66587. $directedGraph =& new Structures_Graph(true);
  66588. $nonDirectedGraph =& new Structures_Graph(false);
  66589. ]]>
  66590. </programlisting>
  66591. and passing the constructor a flag telling it whether the graph should be directed. A directed graph will always be directed during its lifetime. It's a permanent characteristic.
  66592. </para>
  66593. <para>
  66594. To fill out the graph, we'll need to create some nodes, and then call Graph::addNode.
  66595. <programlisting>
  66596. <![CDATA[
  66597. require_once 'Structures/Graph/Node.php';
  66598. $nodeOne =& new Structures_Graph_Node();
  66599. $nodeTwo =& new Structures_Graph_Node();
  66600. $nodeThree =& new Structures_Graph_Node();
  66601. $directedGraph->addNode(&$nodeOne);
  66602. $directedGraph->addNode(&$nodeTwo);
  66603. $directedGraph->addNode(&$nodeThree);
  66604. ]]>
  66605. </programlisting>
  66606. and then setup the arcs:
  66607. <programlisting>
  66608. <![CDATA[
  66609. $nodeOne->connectTo($nodeTwo);
  66610. $nodeOne->connectTo($nodeThree);
  66611. ]]>
  66612. </programlisting>
  66613. Note that arcs can only be created after the nodes have been inserted into the graph.
  66614. </para>
  66615. </refsect1>
  66616. <refsect1 id="{@id package.database.structures_graph.tutorial.nodesanddata}">
  66617. <title>Associating Data</title>
  66618. <para>
  66619. Graphs are only useful as datastructures if they can hold data. Structure_Graph stores data in nodes. Each node contains a setter and a getter for its data.
  66620. <programlisting>
  66621. <![CDATA[
  66622. $nodeOne->setData("Node One's Data is a String");
  66623. $nodeTwo->setData(1976);
  66624. $nodeThree->setData('Some other string');
  66625. print("NodeTwo's Data is an integer: " . $nodeTwo->getData());
  66626. ]]>
  66627. </programlisting>
  66628. </para>
  66629. <para>
  66630. Structure_Graph nodes can also store metadata, alongside with the main data. Metadata differs from regular data just because it is stored under a key, making it possible to store more than one data reference per node. The metadata getter and setter need the key to perform the operation:
  66631. <programlisting>
  66632. <![CDATA[
  66633. $nodeOne->setMetadata('example key', "Node One's Sample Metadata");
  66634. print("Metadata stored under key 'example key' in node one: " . $nodeOne->getMetadata('example key'));
  66635. $nodeOne->unsetMetadata('example key');
  66636. ]]>
  66637. </programlisting>
  66638. </para>
  66639. </refsect1>
  66640. <refsect1 id="{@id package.database.structures_graph.tutorial.querying}">
  66641. <title>Querying a Graph</title>
  66642. <para>
  66643. Structures_Graph provides for basic querying of the graph:
  66644. <programlisting>
  66645. <![CDATA[
  66646. // Nodes are able to calculate their indegree and outdegree
  66647. print("NodeOne's inDegree: " . $nodeOne->inDegree());
  66648. print("NodeOne's outDegree: " . $nodeOne->outDegree());
  66649. // and naturally, nodes can report on their arcs
  66650. $arcs = $nodeOne->getNeighbours();
  66651. for ($i=0;$i<sizeof($arcs);$i++) {
  66652. print("NodeOne has an arc to " . $arcs[$i]->getData());
  66653. }
  66654. ]]>
  66655. </programlisting>
  66656. </para>
  66657. </refsect1>
  66658. </refentry>
  66659. ����������������������������������������������������Structures_Graph-1.2.0/Structures/Graph/Manipulator/AcyclicTest.php���������������������������������0000644�0001750�0001750�00000013201�14627105564�025334� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  66660. /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
  66661. // +-----------------------------------------------------------------------------+
  66662. // | Copyright (c) 2003 Sérgio Gonçalves Carvalho |
  66663. // +-----------------------------------------------------------------------------+
  66664. // | This file is part of Structures_Graph. |
  66665. // | |
  66666. // | Structures_Graph is free software; you can redistribute it and/or modify |
  66667. // | it under the terms of the GNU Lesser General Public License as published by |
  66668. // | the Free Software Foundation; either version 2.1 of the License, or |
  66669. // | (at your option) any later version. |
  66670. // | |
  66671. // | Structures_Graph is distributed in the hope that it will be useful, |
  66672. // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
  66673. // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
  66674. // | GNU Lesser General Public License for more details. |
  66675. // | |
  66676. // | You should have received a copy of the GNU Lesser General Public License |
  66677. // | along with Structures_Graph; if not, write to the Free Software |
  66678. // | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
  66679. // | 02111-1307 USA |
  66680. // +-----------------------------------------------------------------------------+
  66681. // | Author: Sérgio Carvalho <sergio.carvalho@portugalmail.com> |
  66682. // +-----------------------------------------------------------------------------+
  66683. //
  66684. /**
  66685. * This file contains the definition of the Structures_Graph_Manipulator_AcyclicTest graph manipulator.
  66686. *
  66687. * @see Structures_Graph_Manipulator_AcyclicTest
  66688. * @package Structures_Graph
  66689. */
  66690. /* dependencies {{{ */
  66691. /** */
  66692. require_once 'PEAR.php';
  66693. /** */
  66694. require_once 'Structures/Graph.php';
  66695. /** */
  66696. require_once 'Structures/Graph/Node.php';
  66697. /* }}} */
  66698. /* class Structures_Graph_Manipulator_AcyclicTest {{{ */
  66699. /**
  66700. * The Structures_Graph_Manipulator_AcyclicTest is a graph manipulator
  66701. * which tests whether a graph contains a cycle.
  66702. *
  66703. * The definition of an acyclic graph used in this manipulator is that of a
  66704. * DAG. The graph must be directed, or else it is considered cyclic, even when
  66705. * there are no arcs.
  66706. *
  66707. * @author Sérgio Carvalho <sergio.carvalho@portugalmail.com>
  66708. * @copyright (c) 2004 by Sérgio Carvalho
  66709. * @package Structures_Graph
  66710. */
  66711. class Structures_Graph_Manipulator_AcyclicTest {
  66712. /* _nonVisitedInDegree {{{ */
  66713. /**
  66714. *
  66715. * This is a variant of Structures_Graph::inDegree which does
  66716. * not count nodes marked as visited.
  66717. *
  66718. * @return integer Number of non-visited nodes that link to this one
  66719. */
  66720. protected static function _nonVisitedInDegree(&$node) {
  66721. $result = 0;
  66722. $graphNodes =& $node->_graph->getNodes();
  66723. foreach (array_keys($graphNodes) as $key) {
  66724. if ((!$graphNodes[$key]->getMetadata('acyclic-test-visited')) && $graphNodes[$key]->connectsTo($node)) $result++;
  66725. }
  66726. return $result;
  66727. }
  66728. /* }}} */
  66729. /* _isAcyclic {{{ */
  66730. /**
  66731. * Check if the graph is acyclic
  66732. */
  66733. protected static function _isAcyclic(&$graph) {
  66734. // Mark every node as not visited
  66735. $nodes =& $graph->getNodes();
  66736. $nodeKeys = array_keys($nodes);
  66737. $refGenerator = array();
  66738. foreach($nodeKeys as $key) {
  66739. $refGenerator[] = false;
  66740. $nodes[$key]->setMetadata('acyclic-test-visited', $refGenerator[sizeof($refGenerator) - 1]);
  66741. }
  66742. // Iteratively peel off leaf nodes
  66743. do {
  66744. // Find out which nodes are leafs (excluding visited nodes)
  66745. $leafNodes = array();
  66746. foreach($nodeKeys as $key) {
  66747. if ((!$nodes[$key]->getMetadata('acyclic-test-visited')) && Structures_Graph_Manipulator_AcyclicTest::_nonVisitedInDegree($nodes[$key]) == 0) {
  66748. $leafNodes[] =& $nodes[$key];
  66749. }
  66750. }
  66751. // Mark leafs as visited
  66752. for ($i=sizeof($leafNodes) - 1; $i>=0; $i--) {
  66753. $visited =& $leafNodes[$i]->getMetadata('acyclic-test-visited');
  66754. $visited = true;
  66755. $leafNodes[$i]->setMetadata('acyclic-test-visited', $visited);
  66756. }
  66757. } while (sizeof($leafNodes) > 0);
  66758. // If graph is a DAG, there should be no non-visited nodes. Let's try to prove otherwise
  66759. $result = true;
  66760. foreach($nodeKeys as $key) if (!$nodes[$key]->getMetadata('acyclic-test-visited')) $result = false;
  66761. // Cleanup visited marks
  66762. foreach($nodeKeys as $key) $nodes[$key]->unsetMetadata('acyclic-test-visited');
  66763. return $result;
  66764. }
  66765. /* }}} */
  66766. /* isAcyclic {{{ */
  66767. /**
  66768. *
  66769. * isAcyclic returns true if a graph contains no cycles, false otherwise.
  66770. *
  66771. * @return boolean true iff graph is acyclic
  66772. */
  66773. public static function isAcyclic(&$graph) {
  66774. // We only test graphs
  66775. if (!is_a($graph, 'Structures_Graph')) return Pear::raiseError('Structures_Graph_Manipulator_AcyclicTest::isAcyclic received an object that is not a Structures_Graph', STRUCTURES_GRAPH_ERROR_GENERIC);
  66776. if (!$graph->isDirected()) return false; // Only directed graphs may be acyclic
  66777. return Structures_Graph_Manipulator_AcyclicTest::_isAcyclic($graph);
  66778. }
  66779. /* }}} */
  66780. }
  66781. /* }}} */
  66782. ?>
  66783. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Structures_Graph-1.2.0/Structures/Graph/Manipulator/TopologicalSorter.php���������������������������0000644�0001750�0001750�00000015564�14627105564�026616� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  66784. /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
  66785. // +-----------------------------------------------------------------------------+
  66786. // | Copyright (c) 2003 Sérgio Gonçalves Carvalho |
  66787. // +-----------------------------------------------------------------------------+
  66788. // | This file is part of Structures_Graph. |
  66789. // | |
  66790. // | Structures_Graph is free software; you can redistribute it and/or modify |
  66791. // | it under the terms of the GNU Lesser General Public License as published by |
  66792. // | the Free Software Foundation; either version 2.1 of the License, or |
  66793. // | (at your option) any later version. |
  66794. // | |
  66795. // | Structures_Graph is distributed in the hope that it will be useful, |
  66796. // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
  66797. // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
  66798. // | GNU Lesser General Public License for more details. |
  66799. // | |
  66800. // | You should have received a copy of the GNU Lesser General Public License |
  66801. // | along with Structures_Graph; if not, write to the Free Software |
  66802. // | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
  66803. // | 02111-1307 USA |
  66804. // +-----------------------------------------------------------------------------+
  66805. // | Author: Sérgio Carvalho <sergio.carvalho@portugalmail.com> |
  66806. // +-----------------------------------------------------------------------------+
  66807. //
  66808. /**
  66809. * This file contains the definition of the Structures_Graph_Manipulator_TopologicalSorter class.
  66810. *
  66811. * @package Structures_Graph
  66812. */
  66813. require_once 'PEAR.php';
  66814. require_once 'Structures/Graph.php';
  66815. require_once 'Structures/Graph/Node.php';
  66816. require_once 'Structures/Graph/Manipulator/AcyclicTest.php';
  66817. /**
  66818. * The Structures_Graph_Manipulator_TopologicalSorter is a manipulator
  66819. * which is able to return the set of nodes in a graph, sorted by topological
  66820. * order.
  66821. *
  66822. * A graph may only be sorted topologically iff it's a DAG. You can test it
  66823. * with the Structures_Graph_Manipulator_AcyclicTest.
  66824. *
  66825. * @author Sérgio Carvalho <sergio.carvalho@portugalmail.com>
  66826. * @copyright (c) 2004 by Sérgio Carvalho
  66827. * @see Structures_Graph_Manipulator_AcyclicTest
  66828. * @package Structures_Graph
  66829. */
  66830. class Structures_Graph_Manipulator_TopologicalSorter
  66831. {
  66832. /**
  66833. * This is a variant of Structures_Graph::inDegree which does
  66834. * not count nodes marked as visited.
  66835. *
  66836. * @param object $node Node to check
  66837. *
  66838. * @return integer Number of non-visited nodes that link to this one
  66839. */
  66840. protected static function _nonVisitedInDegree(&$node)
  66841. {
  66842. $result = 0;
  66843. $graphNodes =& $node->_graph->getNodes();
  66844. foreach (array_keys($graphNodes) as $key) {
  66845. if ((!$graphNodes[$key]->getMetadata('topological-sort-visited'))
  66846. && $graphNodes[$key]->connectsTo($node)
  66847. ) {
  66848. $result++;
  66849. }
  66850. }
  66851. return $result;
  66852. }
  66853. /**
  66854. * Sort implementation
  66855. *
  66856. * @param object $graph Graph to sort
  66857. *
  66858. * @return void
  66859. */
  66860. protected static function _sort(&$graph)
  66861. {
  66862. // Mark every node as not visited
  66863. $nodes =& $graph->getNodes();
  66864. $nodeKeys = array_keys($nodes);
  66865. $refGenerator = array();
  66866. foreach ($nodeKeys as $key) {
  66867. $refGenerator[] = false;
  66868. $nodes[$key]->setMetadata(
  66869. 'topological-sort-visited',
  66870. $refGenerator[sizeof($refGenerator) - 1]
  66871. );
  66872. }
  66873. // Iteratively peel off leaf nodes
  66874. $topologicalLevel = 0;
  66875. do {
  66876. // Find out which nodes are leafs (excluding visited nodes)
  66877. $leafNodes = array();
  66878. foreach ($nodeKeys as $key) {
  66879. if ((!$nodes[$key]->getMetadata('topological-sort-visited'))
  66880. && static::_nonVisitedInDegree($nodes[$key]) == 0
  66881. ) {
  66882. $leafNodes[] =& $nodes[$key];
  66883. }
  66884. }
  66885. // Mark leafs as visited
  66886. $refGenerator[] = $topologicalLevel;
  66887. for ($i = sizeof($leafNodes) - 1; $i>=0; $i--) {
  66888. $visited =& $leafNodes[$i]->getMetadata('topological-sort-visited');
  66889. $visited = true;
  66890. $leafNodes[$i]->setMetadata('topological-sort-visited', $visited);
  66891. $leafNodes[$i]->setMetadata(
  66892. 'topological-sort-level',
  66893. $refGenerator[sizeof($refGenerator) - 1]
  66894. );
  66895. }
  66896. $topologicalLevel++;
  66897. } while (sizeof($leafNodes) > 0);
  66898. // Cleanup visited marks
  66899. foreach ($nodeKeys as $key) {
  66900. $nodes[$key]->unsetMetadata('topological-sort-visited');
  66901. }
  66902. }
  66903. /**
  66904. * Sort returns the graph's nodes, sorted by topological order.
  66905. *
  66906. * The result is an array with as many entries as topological levels.
  66907. * Each entry in this array is an array of nodes within
  66908. * the given topological level.
  66909. *
  66910. * @param object $graph Graph to sort
  66911. *
  66912. * @return array The graph's nodes, sorted by topological order.
  66913. */
  66914. public static function sort(&$graph)
  66915. {
  66916. // We only sort graphs
  66917. if (!is_a($graph, 'Structures_Graph')) {
  66918. return Pear::raiseError(
  66919. 'Structures_Graph_Manipulator_TopologicalSorter::sort received'
  66920. . ' an object that is not a Structures_Graph',
  66921. STRUCTURES_GRAPH_ERROR_GENERIC
  66922. );
  66923. }
  66924. if (!Structures_Graph_Manipulator_AcyclicTest::isAcyclic($graph)) {
  66925. return Pear::raiseError(
  66926. 'Structures_Graph_Manipulator_TopologicalSorter::sort'
  66927. . ' received an graph that has cycles',
  66928. STRUCTURES_GRAPH_ERROR_GENERIC
  66929. );
  66930. }
  66931. Structures_Graph_Manipulator_TopologicalSorter::_sort($graph);
  66932. $result = array();
  66933. // Fill out result array
  66934. $nodes =& $graph->getNodes();
  66935. $nodeKeys = array_keys($nodes);
  66936. foreach ($nodeKeys as $key) {
  66937. if (!array_key_exists($nodes[$key]->getMetadata('topological-sort-level'), $result)) {
  66938. $result[$nodes[$key]->getMetadata('topological-sort-level')]
  66939. = array();
  66940. }
  66941. $result[$nodes[$key]->getMetadata('topological-sort-level')][]
  66942. =& $nodes[$key];
  66943. $nodes[$key]->unsetMetadata('topological-sort-level');
  66944. }
  66945. return $result;
  66946. }
  66947. }
  66948. ?>
  66949. ��������������������������������������������������������������������������������������������������������������������������������������������Structures_Graph-1.2.0/Structures/Graph/Node.php����������������������������������������������������0000644�0001750�0001750�00000025440�14627105564�021527� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  66950. /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
  66951. // +-----------------------------------------------------------------------------+
  66952. // | Copyright (c) 2003 Sérgio Gonçalves Carvalho |
  66953. // +-----------------------------------------------------------------------------+
  66954. // | This file is part of Structures_Graph. |
  66955. // | |
  66956. // | Structures_Graph is free software; you can redistribute it and/or modify |
  66957. // | it under the terms of the GNU Lesser General Public License as published by |
  66958. // | the Free Software Foundation; either version 2.1 of the License, or |
  66959. // | (at your option) any later version. |
  66960. // | |
  66961. // | Structures_Graph is distributed in the hope that it will be useful, |
  66962. // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
  66963. // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
  66964. // | GNU Lesser General Public License for more details. |
  66965. // | |
  66966. // | You should have received a copy of the GNU Lesser General Public License |
  66967. // | along with Structures_Graph; if not, write to the Free Software |
  66968. // | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
  66969. // | 02111-1307 USA |
  66970. // +-----------------------------------------------------------------------------+
  66971. // | Author: Sérgio Carvalho <sergio.carvalho@portugalmail.com> |
  66972. // +-----------------------------------------------------------------------------+
  66973. //
  66974. /**
  66975. * This file contains the definition of the Structures_Graph_Node class
  66976. *
  66977. * @see Structures_Graph_Node
  66978. * @package Structures_Graph
  66979. */
  66980. /* dependencies {{{ */
  66981. /** */
  66982. require_once 'PEAR.php';
  66983. /** */
  66984. require_once 'Structures/Graph.php';
  66985. /* }}} */
  66986. /* class Structures_Graph_Node {{{ */
  66987. /**
  66988. * The Structures_Graph_Node class represents a Node that can be member of a
  66989. * graph node set.
  66990. *
  66991. * A graph node can contain data. Under this API, the node contains default data,
  66992. * and key index data. It behaves, thus, both as a regular data node, and as a
  66993. * dictionary (or associative array) node.
  66994. *
  66995. * Regular data is accessed via getData and setData. Key indexed data is accessed
  66996. * via getMetadata and setMetadata.
  66997. *
  66998. * @author Sérgio Carvalho <sergio.carvalho@portugalmail.com>
  66999. * @copyright (c) 2004 by Sérgio Carvalho
  67000. * @package Structures_Graph
  67001. */
  67002. /* }}} */
  67003. class Structures_Graph_Node {
  67004. /* fields {{{ */
  67005. /**
  67006. * @access private
  67007. */
  67008. var $_data = null;
  67009. /** @access private */
  67010. var $_metadata = array();
  67011. /** @access private */
  67012. var $_arcs = array();
  67013. /** @access private */
  67014. var $_graph = null;
  67015. /* }}} */
  67016. /* Constructor {{{ */
  67017. /**
  67018. *
  67019. * Constructor
  67020. *
  67021. * @access public
  67022. */
  67023. function __construct() {
  67024. }
  67025. /* }}} */
  67026. /* getGraph {{{ */
  67027. /**
  67028. *
  67029. * Node graph getter
  67030. *
  67031. * @return Structures_Graph Graph where node is stored
  67032. * @access public
  67033. */
  67034. function &getGraph() {
  67035. return $this->_graph;
  67036. }
  67037. /* }}} */
  67038. /* setGraph {{{ */
  67039. /**
  67040. *
  67041. * Node graph setter. This method should not be called directly. Use Graph::addNode instead.
  67042. *
  67043. * @param Structures_Graph Set the graph for this node.
  67044. * @see Structures_Graph::addNode()
  67045. * @access public
  67046. */
  67047. function setGraph(&$graph) {
  67048. $this->_graph =& $graph;
  67049. }
  67050. /* }}} */
  67051. /* getData {{{ */
  67052. /**
  67053. *
  67054. * Node data getter.
  67055. *
  67056. * Each graph node can contain a reference to one variable. This is the getter for that reference.
  67057. *
  67058. * @return mixed Data stored in node
  67059. * @access public
  67060. */
  67061. function &getData() {
  67062. return $this->_data;
  67063. }
  67064. /* }}} */
  67065. /* setData {{{ */
  67066. /**
  67067. *
  67068. * Node data setter
  67069. *
  67070. * Each graph node can contain a reference to one variable. This is the setter for that reference.
  67071. *
  67072. * @return mixed Data to store in node
  67073. * @access public
  67074. */
  67075. function setData(&$data) {
  67076. $this->_data =& $data;
  67077. }
  67078. /* }}} */
  67079. /* metadataKeyExists {{{ */
  67080. /**
  67081. *
  67082. * Test for existence of metadata under a given key.
  67083. *
  67084. * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an
  67085. * associative array or in a dictionary. This method tests whether a given metadata key exists for this node.
  67086. *
  67087. * @param string Key to test
  67088. * @return boolean
  67089. * @access public
  67090. */
  67091. function metadataKeyExists($key) {
  67092. return array_key_exists($key, $this->_metadata);
  67093. }
  67094. /* }}} */
  67095. /* getMetadata {{{ */
  67096. /**
  67097. *
  67098. * Node metadata getter
  67099. *
  67100. * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an
  67101. * associative array or in a dictionary. This method gets the data under the given key. If the key does
  67102. * not exist, an error will be thrown, so testing using metadataKeyExists might be needed.
  67103. *
  67104. * @param string Key
  67105. * @param boolean nullIfNonexistent (defaults to false).
  67106. * @return mixed Metadata Data stored in node under given key
  67107. * @see metadataKeyExists
  67108. * @access public
  67109. */
  67110. function &getMetadata($key, $nullIfNonexistent = false) {
  67111. if (array_key_exists($key, $this->_metadata)) {
  67112. return $this->_metadata[$key];
  67113. } else {
  67114. if ($nullIfNonexistent) {
  67115. $a = null;
  67116. return $a;
  67117. } else {
  67118. $a = Pear::raiseError('Structures_Graph_Node::getMetadata: Requested key does not exist', STRUCTURES_GRAPH_ERROR_GENERIC);
  67119. return $a;
  67120. }
  67121. }
  67122. }
  67123. /* }}} */
  67124. /* unsetMetadata {{{ */
  67125. /**
  67126. *
  67127. * Delete metadata by key
  67128. *
  67129. * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an
  67130. * associative array or in a dictionary. This method removes any data that might be stored under the provided key.
  67131. * If the key does not exist, no error is thrown, so it is safe using this method without testing for key existence.
  67132. *
  67133. * @param string Key
  67134. * @access public
  67135. */
  67136. function unsetMetadata($key) {
  67137. if (array_key_exists($key, $this->_metadata)) unset($this->_metadata[$key]);
  67138. }
  67139. /* }}} */
  67140. /* setMetadata {{{ */
  67141. /**
  67142. *
  67143. * Node metadata setter
  67144. *
  67145. * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an
  67146. * associative array or in a dictionary. This method stores data under the given key. If the key already exists,
  67147. * previously stored data is discarded.
  67148. *
  67149. * @param string Key
  67150. * @param mixed Data
  67151. * @access public
  67152. */
  67153. function setMetadata($key, &$data) {
  67154. $this->_metadata[$key] =& $data;
  67155. }
  67156. /* }}} */
  67157. /* _connectTo {{{ */
  67158. /** @access private */
  67159. function _connectTo(&$destinationNode) {
  67160. $this->_arcs[] =& $destinationNode;
  67161. }
  67162. /* }}} */
  67163. /* connectTo {{{ */
  67164. /**
  67165. *
  67166. * Connect this node to another one.
  67167. *
  67168. * If the graph is not directed, the reverse arc, connecting $destinationNode to $this is also created.
  67169. *
  67170. * @param Structures_Graph_Node Node to connect to
  67171. * @access public
  67172. */
  67173. function connectTo(&$destinationNode) {
  67174. // We only connect to nodes
  67175. if (!is_a($destinationNode, 'Structures_Graph_Node')) return Pear::raiseError('Structures_Graph_Node::connectTo received an object that is not a Structures_Graph_Node', STRUCTURES_GRAPH_ERROR_GENERIC);
  67176. // Nodes must already be in graphs to be connected
  67177. if ($this->_graph == null) return Pear::raiseError('Structures_Graph_Node::connectTo Tried to connect a node that is not in a graph', STRUCTURES_GRAPH_ERROR_GENERIC);
  67178. if ($destinationNode->getGraph() == null) return Pear::raiseError('Structures_Graph_Node::connectTo Tried to connect to a node that is not in a graph', STRUCTURES_GRAPH_ERROR_GENERIC);
  67179. // Connect here
  67180. $this->_connectTo($destinationNode);
  67181. // If graph is undirected, connect back
  67182. if (!$this->_graph->isDirected()) {
  67183. $destinationNode->_connectTo($this);
  67184. }
  67185. }
  67186. /* }}} */
  67187. /* getNeighbours {{{ */
  67188. /**
  67189. *
  67190. * Return nodes connected to this one.
  67191. *
  67192. * @return array Array of nodes
  67193. * @access public
  67194. */
  67195. function getNeighbours() {
  67196. return $this->_arcs;
  67197. }
  67198. /* }}} */
  67199. /* connectsTo {{{ */
  67200. /**
  67201. *
  67202. * Test wether this node has an arc to the target node
  67203. *
  67204. * @return boolean True if the two nodes are connected
  67205. * @access public
  67206. */
  67207. function connectsTo(&$target) {
  67208. if (version_compare(PHP_VERSION, '5.0.0') >= 0) {
  67209. return in_array($target, $this->getNeighbours(), true);
  67210. }
  67211. $copy = $target;
  67212. $arcKeys = array_keys($this->_arcs);
  67213. foreach($arcKeys as $key) {
  67214. /* ZE1 chokes on this expression:
  67215. if ($target === $arc) return true;
  67216. so, we'll use more convoluted stuff
  67217. */
  67218. $arc =& $this->_arcs[$key];
  67219. $target = true;
  67220. if ($arc === true) {
  67221. $target = false;
  67222. if ($arc === false) {
  67223. $target = $copy;
  67224. return true;
  67225. }
  67226. }
  67227. }
  67228. $target = $copy;
  67229. return false;
  67230. }
  67231. /* }}} */
  67232. /* inDegree {{{ */
  67233. /**
  67234. *
  67235. * Calculate the in degree of the node.
  67236. *
  67237. * The indegree for a node is the number of arcs entering the node. For non directed graphs,
  67238. * the indegree is equal to the outdegree.
  67239. *
  67240. * @return integer In degree of the node
  67241. * @access public
  67242. */
  67243. function inDegree() {
  67244. if ($this->_graph == null) return 0;
  67245. if (!$this->_graph->isDirected()) return $this->outDegree();
  67246. $result = 0;
  67247. $graphNodes =& $this->_graph->getNodes();
  67248. foreach (array_keys($graphNodes) as $key) {
  67249. if ($graphNodes[$key]->connectsTo($this)) $result++;
  67250. }
  67251. return $result;
  67252. }
  67253. /* }}} */
  67254. /* outDegree {{{ */
  67255. /**
  67256. *
  67257. * Calculate the out degree of the node.
  67258. *
  67259. * The outdegree for a node is the number of arcs exiting the node. For non directed graphs,
  67260. * the outdegree is always equal to the indegree.
  67261. *
  67262. * @return integer Out degree of the node
  67263. * @access public
  67264. */
  67265. function outDegree() {
  67266. if ($this->_graph == null) return 0;
  67267. return sizeof($this->_arcs);
  67268. }
  67269. /* }}} */
  67270. }
  67271. ?>
  67272. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Structures_Graph-1.2.0/Structures/Graph.php���������������������������������������������������������0000644�0001750�0001750�00000014037�14627105564�020642� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  67273. /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
  67274. // +-----------------------------------------------------------------------------+
  67275. // | Copyright (c) 2003 Sérgio Gonçalves Carvalho |
  67276. // +-----------------------------------------------------------------------------+
  67277. // | This file is part of Structures_Graph. |
  67278. // | |
  67279. // | Structures_Graph is free software; you can redistribute it and/or modify |
  67280. // | it under the terms of the GNU Lesser General Public License as published by |
  67281. // | the Free Software Foundation; either version 2.1 of the License, or |
  67282. // | (at your option) any later version. |
  67283. // | |
  67284. // | Structures_Graph is distributed in the hope that it will be useful, |
  67285. // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
  67286. // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
  67287. // | GNU Lesser General Public License for more details. |
  67288. // | |
  67289. // | You should have received a copy of the GNU Lesser General Public License |
  67290. // | along with Structures_Graph; if not, write to the Free Software |
  67291. // | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
  67292. // | 02111-1307 USA |
  67293. // +-----------------------------------------------------------------------------+
  67294. // | Author: Sérgio Carvalho <sergio.carvalho@portugalmail.com> |
  67295. // +-----------------------------------------------------------------------------+
  67296. //
  67297. /**
  67298. * The Graph.php file contains the definition of the Structures_Graph class
  67299. *
  67300. * @package Structures_Graph
  67301. */
  67302. /* dependencies {{{ */
  67303. require_once 'PEAR.php';
  67304. require_once 'Structures/Graph/Node.php';
  67305. /* }}} */
  67306. define('STRUCTURES_GRAPH_ERROR_GENERIC', 100);
  67307. /* class Structures_Graph {{{ */
  67308. /**
  67309. * The Structures_Graph class represents a graph data structure.
  67310. *
  67311. * A Graph is a data structure composed by a set of nodes, connected by arcs.
  67312. * Graphs may either be directed or undirected. In a directed graph, arcs are
  67313. * directional, and can be traveled only one way. In an undirected graph, arcs
  67314. * are bidirectional, and can be traveled both ways.
  67315. *
  67316. * @author Sérgio Carvalho <sergio.carvalho@portugalmail.com>
  67317. * @copyright (c) 2004 by Sérgio Carvalho
  67318. * @package Structures_Graph
  67319. */
  67320. /* }}} */
  67321. class Structures_Graph
  67322. {
  67323. /**
  67324. * List of node objects in this graph
  67325. * @access private
  67326. */
  67327. var $_nodes = array();
  67328. /**
  67329. * If the graph is directed or not
  67330. * @access private
  67331. */
  67332. var $_directed = false;
  67333. /**
  67334. * Constructor
  67335. *
  67336. * @param boolean $directed Set to true if the graph is directed.
  67337. * Set to false if it is not directed.
  67338. */
  67339. public function __construct($directed = true)
  67340. {
  67341. $this->_directed = $directed;
  67342. }
  67343. /**
  67344. * Old constructor (PHP4-style; kept for BC with extending classes)
  67345. *
  67346. * @param boolean $directed Set to true if the graph is directed.
  67347. * Set to false if it is not directed.
  67348. *
  67349. * @return void
  67350. */
  67351. public function Structures_Graph($directed = true)
  67352. {
  67353. $this->__construct($directed);
  67354. }
  67355. /**
  67356. * Return true if a graph is directed
  67357. *
  67358. * @return boolean true if the graph is directed
  67359. */
  67360. public function isDirected()
  67361. {
  67362. return (boolean) $this->_directed;
  67363. }
  67364. /**
  67365. * Add a Node to the Graph
  67366. *
  67367. * @param Structures_Graph_Node $newNode The node to be added.
  67368. *
  67369. * @return void
  67370. */
  67371. public function addNode(&$newNode)
  67372. {
  67373. // We only add nodes
  67374. if (!is_a($newNode, 'Structures_Graph_Node')) {
  67375. return Pear::raiseError(
  67376. 'Structures_Graph::addNode received an object that is not'
  67377. . ' a Structures_Graph_Node',
  67378. STRUCTURES_GRAPH_ERROR_GENERIC
  67379. );
  67380. }
  67381. //Graphs are node *sets*, so duplicates are forbidden.
  67382. // We allow nodes that are exactly equal, but disallow equal references.
  67383. foreach ($this->_nodes as $key => $node) {
  67384. /*
  67385. ZE1 equality operators choke on the recursive cycle introduced
  67386. by the _graph field in the Node object.
  67387. So, we'll check references the hard way
  67388. (change $this->_nodes[$key] and check if the change reflects in
  67389. $node)
  67390. */
  67391. $savedData = $this->_nodes[$key];
  67392. $referenceIsEqualFlag = false;
  67393. $this->_nodes[$key] = true;
  67394. if ($node === true) {
  67395. $this->_nodes[$key] = false;
  67396. if ($node === false) {
  67397. $referenceIsEqualFlag = true;
  67398. }
  67399. }
  67400. $this->_nodes[$key] = $savedData;
  67401. if ($referenceIsEqualFlag) {
  67402. return Pear::raiseError(
  67403. 'Structures_Graph::addNode received an object that is'
  67404. . ' a duplicate for this dataset',
  67405. STRUCTURES_GRAPH_ERROR_GENERIC
  67406. );
  67407. }
  67408. }
  67409. $this->_nodes[] =& $newNode;
  67410. $newNode->setGraph($this);
  67411. }
  67412. /**
  67413. * Remove a Node from the Graph
  67414. *
  67415. * @param Structures_Graph_Node $node The node to be removed from the graph
  67416. *
  67417. * @return void
  67418. * @todo This is unimplemented
  67419. */
  67420. public function removeNode(&$node)
  67421. {
  67422. }
  67423. /**
  67424. * Return the node set, in no particular order.
  67425. * For ordered node sets, use a Graph Manipulator insted.
  67426. *
  67427. * @return array The set of nodes in this graph
  67428. * @see Structures_Graph_Manipulator_TopologicalSorter
  67429. */
  67430. public function &getNodes()
  67431. {
  67432. return $this->_nodes;
  67433. }
  67434. }
  67435. ?>
  67436. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Structures_Graph-1.2.0/tests/BasicGraphTest.php�����������������������������������������������������0000644�0001750�0001750�00000021605�14627105564�021422� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  67437. /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
  67438. // +-----------------------------------------------------------------------------+
  67439. // | Copyright (c) 2003 Sérgio Gonçalves Carvalho |
  67440. // +-----------------------------------------------------------------------------+
  67441. // | This file is part of Structures_Graph. |
  67442. // | |
  67443. // | Structures_Graph is free software; you can redistribute it and/or modify |
  67444. // | it under the terms of the GNU Lesser General Public License as published by |
  67445. // | the Free Software Foundation; either version 2.1 of the License, or |
  67446. // | (at your option) any later version. |
  67447. // | |
  67448. // | Structures_Graph is distributed in the hope that it will be useful, |
  67449. // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
  67450. // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
  67451. // | GNU Lesser General Public License for more details. |
  67452. // | |
  67453. // | You should have received a copy of the GNU Lesser General Public License |
  67454. // | along with Structures_Graph; if not, write to the Free Software |
  67455. // | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
  67456. // | 02111-1307 USA |
  67457. // +-----------------------------------------------------------------------------+
  67458. // | Author: Sérgio Carvalho <sergio.carvalho@portugalmail.com> |
  67459. // +-----------------------------------------------------------------------------+
  67460. //
  67461. require_once dirname(__FILE__) . '/helper.inc';
  67462. /**
  67463. * @access private
  67464. */
  67465. class BasicGraph extends \PHPUnit\Framework\TestCase
  67466. {
  67467. var $_graph = null;
  67468. function test_create_graph() {
  67469. $this->_graph = new Structures_Graph();
  67470. $this->assertTrue(is_a($this->_graph, 'Structures_Graph'));
  67471. }
  67472. function test_add_node() {
  67473. $this->_graph = new Structures_Graph();
  67474. $data = 1;
  67475. $node = new Structures_Graph_Node($data);
  67476. $this->_graph->addNode($node);
  67477. $node = new Structures_Graph_Node($data);
  67478. $this->_graph->addNode($node);
  67479. $node = new Structures_Graph_Node($data);
  67480. $this->_graph->addNode($node);
  67481. $this->assertTrue(true);
  67482. }
  67483. function test_connect_node() {
  67484. $this->_graph = new Structures_Graph();
  67485. $data = 1;
  67486. $node1 = new Structures_Graph_Node($data);
  67487. $node2 = new Structures_Graph_Node($data);
  67488. $this->_graph->addNode($node1);
  67489. $this->_graph->addNode($node2);
  67490. $node1->connectTo($node2);
  67491. $node =& $this->_graph->getNodes();
  67492. $node =& $node[0];
  67493. $node = $node->getNeighbours();
  67494. $node =& $node[0];
  67495. /*
  67496. ZE1 == and === operators fail on $node,$node2 because of the recursion introduced
  67497. by the _graph field in the Node object. So, we'll use the stupid method for reference
  67498. testing
  67499. */
  67500. $node = true;
  67501. $this->assertTrue($node2);
  67502. $node = false;
  67503. $this->assertFalse($node2);
  67504. }
  67505. function test_data_references() {
  67506. $this->_graph = new Structures_Graph();
  67507. $data = 1;
  67508. $node = new Structures_Graph_Node();
  67509. $node->setData($data);
  67510. $this->_graph->addNode($node);
  67511. $data = 2;
  67512. $dataInNode =& $this->_graph->getNodes();
  67513. $dataInNode =& $dataInNode[0];
  67514. $dataInNode =& $dataInNode->getData();
  67515. $this->assertEquals($data, $dataInNode);
  67516. }
  67517. function test_metadata_references() {
  67518. $this->_graph = new Structures_Graph();
  67519. $data = 1;
  67520. $node = new Structures_Graph_Node();
  67521. $node->setMetadata('5', $data);
  67522. $data = 2;
  67523. $dataInNode =& $node->getMetadata('5');
  67524. $this->assertEquals($data, $dataInNode);
  67525. }
  67526. function test_metadata_key_exists() {
  67527. $this->_graph = new Structures_Graph();
  67528. $data = 1;
  67529. $node = new Structures_Graph_Node();
  67530. $node->setMetadata('5', $data);
  67531. $this->assertTrue($node->metadataKeyExists('5'));
  67532. $this->assertFalse($node->metadataKeyExists('1'));
  67533. }
  67534. function test_directed_degree() {
  67535. $this->_graph = new Structures_Graph(true);
  67536. $node = array();
  67537. $node[] = new Structures_Graph_Node();
  67538. $node[] = new Structures_Graph_Node();
  67539. $node[] = new Structures_Graph_Node();
  67540. $this->_graph->addNode($node[0]);
  67541. $this->_graph->addNode($node[1]);
  67542. $this->_graph->addNode($node[2]);
  67543. $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 0 arcs');
  67544. $this->assertEquals(0, $node[1]->inDegree(), 'inDegree test failed for node 1 with 0 arcs');
  67545. $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 0 arcs');
  67546. $this->assertEquals(0, $node[0]->outDegree(), 'outDegree test failed for node 0 with 0 arcs');
  67547. $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 0 arcs');
  67548. $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 0 arcs');
  67549. $node[0]->connectTo($node[1]);
  67550. $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 1 arc');
  67551. $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 1 arc');
  67552. $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 1 arc');
  67553. $this->assertEquals(1, $node[0]->outDegree(), 'outDegree test failed for node 0 with 1 arc');
  67554. $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 1 arc');
  67555. $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 1 arc');
  67556. $node[0]->connectTo($node[2]);
  67557. $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 2 arcs');
  67558. $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 2 arcs');
  67559. $this->assertEquals(1, $node[2]->inDegree(), 'inDegree test failed for node 2 with 2 arcs');
  67560. $this->assertEquals(2, $node[0]->outDegree(), 'outDegree test failed for node 0 with 2 arcs');
  67561. $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 2 arcs');
  67562. $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 2 arcs');
  67563. }
  67564. function test_undirected_degree() {
  67565. $this->_graph = new Structures_Graph(false);
  67566. $node = array();
  67567. $node[] = new Structures_Graph_Node();
  67568. $node[] = new Structures_Graph_Node();
  67569. $node[] = new Structures_Graph_Node();
  67570. $this->_graph->addNode($node[0]);
  67571. $this->_graph->addNode($node[1]);
  67572. $this->_graph->addNode($node[2]);
  67573. $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 0 arcs');
  67574. $this->assertEquals(0, $node[1]->inDegree(), 'inDegree test failed for node 1 with 0 arcs');
  67575. $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 0 arcs');
  67576. $this->assertEquals(0, $node[0]->outDegree(), 'outDegree test failed for node 0 with 0 arcs');
  67577. $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 0 arcs');
  67578. $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 0 arcs');
  67579. $node[0]->connectTo($node[1]);
  67580. $this->assertEquals(1, $node[0]->inDegree(), 'inDegree test failed for node 0 with 1 arc');
  67581. $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 1 arc');
  67582. $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 1 arc');
  67583. $this->assertEquals(1, $node[0]->outDegree(), 'outDegree test failed for node 0 with 1 arc');
  67584. $this->assertEquals(1, $node[1]->outDegree(), 'outDegree test failed for node 1 with 1 arc');
  67585. $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 1 arc');
  67586. $node[0]->connectTo($node[2]);
  67587. $this->assertEquals(2, $node[0]->inDegree(), 'inDegree test failed for node 0 with 2 arcs');
  67588. $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 2 arcs');
  67589. $this->assertEquals(1, $node[2]->inDegree(), 'inDegree test failed for node 2 with 2 arcs');
  67590. $this->assertEquals(2, $node[0]->outDegree(), 'outDegree test failed for node 0 with 2 arcs');
  67591. $this->assertEquals(1, $node[1]->outDegree(), 'outDegree test failed for node 1 with 2 arcs');
  67592. $this->assertEquals(1, $node[2]->outDegree(), 'outDegree test failed for node 2 with 2 arcs');
  67593. }
  67594. }
  67595. ?>
  67596. ���������������������������������������������������������������������������������������������������������������������������Structures_Graph-1.2.0/tests/TopologicalSorterTest.php����������������������������������������������0000644�0001750�0001750�00000003526�14627105564�023074� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  67597. require_once dirname(__FILE__) . '/helper.inc';
  67598. require_once 'Structures/Graph/Manipulator/TopologicalSorter.php';
  67599. class TopologicalSorterTest extends \PHPUnit\Framework\TestCase
  67600. {
  67601. public function testSort()
  67602. {
  67603. $graph = new Structures_Graph();
  67604. $name1 = 'node1';
  67605. $node1 = new Structures_Graph_Node();
  67606. $node1->setData($name1);
  67607. $graph->addNode($node1);
  67608. $name11 = 'node11';
  67609. $node11 = new Structures_Graph_Node();
  67610. $node11->setData($name11);
  67611. $graph->addNode($node11);
  67612. $node1->connectTo($node11);
  67613. $name12 = 'node12';
  67614. $node12 = new Structures_Graph_Node();
  67615. $node12->setData($name12);
  67616. $graph->addNode($node12);
  67617. $node1->connectTo($node12);
  67618. $name121 = 'node121';
  67619. $node121 = new Structures_Graph_Node();
  67620. $node121->setData($name121);
  67621. $graph->addNode($node121);
  67622. $node12->connectTo($node121);
  67623. $name2 = 'node2';
  67624. $node2 = new Structures_Graph_Node();
  67625. $node2->setData($name2);
  67626. $graph->addNode($node2);
  67627. $name21 = 'node21';
  67628. $node21 = new Structures_Graph_Node();
  67629. $node21->setData($name21);
  67630. $graph->addNode($node21);
  67631. $node2->connectTo($node21);
  67632. $nodes = Structures_Graph_Manipulator_TopologicalSorter::sort($graph);
  67633. $this->assertCount(2, $nodes[0]);
  67634. $this->assertEquals('node1', $nodes[0][0]->getData());
  67635. $this->assertEquals('node2', $nodes[0][1]->getData());
  67636. $this->assertCount(3, $nodes[1]);
  67637. $this->assertEquals('node11', $nodes[1][0]->getData());
  67638. $this->assertEquals('node12', $nodes[1][1]->getData());
  67639. $this->assertEquals('node21', $nodes[1][2]->getData());
  67640. $this->assertCount(1, $nodes[2]);
  67641. $this->assertEquals('node121', $nodes[2][0]->getData());
  67642. }
  67643. }
  67644. ?>
  67645. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������Structures_Graph-1.2.0/tests/AcyclicTestTest.php����������������������������������������������������0000644�0001750�0001750�00000002435�14627105564�021626� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  67646. require_once dirname(__FILE__) . '/helper.inc';
  67647. require_once 'Structures/Graph/Manipulator/AcyclicTest.php';
  67648. class AcyclicTestTest extends \PHPUnit\Framework\TestCase
  67649. {
  67650. public function testIsAcyclicFalse()
  67651. {
  67652. $graph = new Structures_Graph();
  67653. $node1 = new Structures_Graph_Node();
  67654. $graph->addNode($node1);
  67655. $node2 = new Structures_Graph_Node();
  67656. $graph->addNode($node2);
  67657. $node1->connectTo($node2);
  67658. $node3 = new Structures_Graph_Node();
  67659. $graph->addNode($node3);
  67660. $node2->connectTo($node3);
  67661. $node3->connectTo($node1);
  67662. $this->assertFalse(
  67663. Structures_Graph_Manipulator_AcyclicTest::isAcyclic($graph),
  67664. 'Graph is cyclic'
  67665. );
  67666. }
  67667. public function testIsAcyclicTrue()
  67668. {
  67669. $graph = new Structures_Graph();
  67670. $node1 = new Structures_Graph_Node();
  67671. $graph->addNode($node1);
  67672. $node2 = new Structures_Graph_Node();
  67673. $graph->addNode($node2);
  67674. $node1->connectTo($node2);
  67675. $node3 = new Structures_Graph_Node();
  67676. $graph->addNode($node3);
  67677. $node2->connectTo($node3);
  67678. $this->assertTrue(
  67679. Structures_Graph_Manipulator_AcyclicTest::isAcyclic($graph),
  67680. 'Graph is acyclic'
  67681. );
  67682. }
  67683. }
  67684. ?>
  67685. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Structures_Graph-1.2.0/tests/BasicGraphTest.php�����������������������������������������������������0000644�0001750�0001750�00000021605�14627105564�021422� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  67686. /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
  67687. // +-----------------------------------------------------------------------------+
  67688. // | Copyright (c) 2003 Sérgio Gonçalves Carvalho |
  67689. // +-----------------------------------------------------------------------------+
  67690. // | This file is part of Structures_Graph. |
  67691. // | |
  67692. // | Structures_Graph is free software; you can redistribute it and/or modify |
  67693. // | it under the terms of the GNU Lesser General Public License as published by |
  67694. // | the Free Software Foundation; either version 2.1 of the License, or |
  67695. // | (at your option) any later version. |
  67696. // | |
  67697. // | Structures_Graph is distributed in the hope that it will be useful, |
  67698. // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
  67699. // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
  67700. // | GNU Lesser General Public License for more details. |
  67701. // | |
  67702. // | You should have received a copy of the GNU Lesser General Public License |
  67703. // | along with Structures_Graph; if not, write to the Free Software |
  67704. // | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
  67705. // | 02111-1307 USA |
  67706. // +-----------------------------------------------------------------------------+
  67707. // | Author: Sérgio Carvalho <sergio.carvalho@portugalmail.com> |
  67708. // +-----------------------------------------------------------------------------+
  67709. //
  67710. require_once dirname(__FILE__) . '/helper.inc';
  67711. /**
  67712. * @access private
  67713. */
  67714. class BasicGraph extends \PHPUnit\Framework\TestCase
  67715. {
  67716. var $_graph = null;
  67717. function test_create_graph() {
  67718. $this->_graph = new Structures_Graph();
  67719. $this->assertTrue(is_a($this->_graph, 'Structures_Graph'));
  67720. }
  67721. function test_add_node() {
  67722. $this->_graph = new Structures_Graph();
  67723. $data = 1;
  67724. $node = new Structures_Graph_Node($data);
  67725. $this->_graph->addNode($node);
  67726. $node = new Structures_Graph_Node($data);
  67727. $this->_graph->addNode($node);
  67728. $node = new Structures_Graph_Node($data);
  67729. $this->_graph->addNode($node);
  67730. $this->assertTrue(true);
  67731. }
  67732. function test_connect_node() {
  67733. $this->_graph = new Structures_Graph();
  67734. $data = 1;
  67735. $node1 = new Structures_Graph_Node($data);
  67736. $node2 = new Structures_Graph_Node($data);
  67737. $this->_graph->addNode($node1);
  67738. $this->_graph->addNode($node2);
  67739. $node1->connectTo($node2);
  67740. $node =& $this->_graph->getNodes();
  67741. $node =& $node[0];
  67742. $node = $node->getNeighbours();
  67743. $node =& $node[0];
  67744. /*
  67745. ZE1 == and === operators fail on $node,$node2 because of the recursion introduced
  67746. by the _graph field in the Node object. So, we'll use the stupid method for reference
  67747. testing
  67748. */
  67749. $node = true;
  67750. $this->assertTrue($node2);
  67751. $node = false;
  67752. $this->assertFalse($node2);
  67753. }
  67754. function test_data_references() {
  67755. $this->_graph = new Structures_Graph();
  67756. $data = 1;
  67757. $node = new Structures_Graph_Node();
  67758. $node->setData($data);
  67759. $this->_graph->addNode($node);
  67760. $data = 2;
  67761. $dataInNode =& $this->_graph->getNodes();
  67762. $dataInNode =& $dataInNode[0];
  67763. $dataInNode =& $dataInNode->getData();
  67764. $this->assertEquals($data, $dataInNode);
  67765. }
  67766. function test_metadata_references() {
  67767. $this->_graph = new Structures_Graph();
  67768. $data = 1;
  67769. $node = new Structures_Graph_Node();
  67770. $node->setMetadata('5', $data);
  67771. $data = 2;
  67772. $dataInNode =& $node->getMetadata('5');
  67773. $this->assertEquals($data, $dataInNode);
  67774. }
  67775. function test_metadata_key_exists() {
  67776. $this->_graph = new Structures_Graph();
  67777. $data = 1;
  67778. $node = new Structures_Graph_Node();
  67779. $node->setMetadata('5', $data);
  67780. $this->assertTrue($node->metadataKeyExists('5'));
  67781. $this->assertFalse($node->metadataKeyExists('1'));
  67782. }
  67783. function test_directed_degree() {
  67784. $this->_graph = new Structures_Graph(true);
  67785. $node = array();
  67786. $node[] = new Structures_Graph_Node();
  67787. $node[] = new Structures_Graph_Node();
  67788. $node[] = new Structures_Graph_Node();
  67789. $this->_graph->addNode($node[0]);
  67790. $this->_graph->addNode($node[1]);
  67791. $this->_graph->addNode($node[2]);
  67792. $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 0 arcs');
  67793. $this->assertEquals(0, $node[1]->inDegree(), 'inDegree test failed for node 1 with 0 arcs');
  67794. $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 0 arcs');
  67795. $this->assertEquals(0, $node[0]->outDegree(), 'outDegree test failed for node 0 with 0 arcs');
  67796. $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 0 arcs');
  67797. $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 0 arcs');
  67798. $node[0]->connectTo($node[1]);
  67799. $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 1 arc');
  67800. $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 1 arc');
  67801. $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 1 arc');
  67802. $this->assertEquals(1, $node[0]->outDegree(), 'outDegree test failed for node 0 with 1 arc');
  67803. $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 1 arc');
  67804. $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 1 arc');
  67805. $node[0]->connectTo($node[2]);
  67806. $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 2 arcs');
  67807. $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 2 arcs');
  67808. $this->assertEquals(1, $node[2]->inDegree(), 'inDegree test failed for node 2 with 2 arcs');
  67809. $this->assertEquals(2, $node[0]->outDegree(), 'outDegree test failed for node 0 with 2 arcs');
  67810. $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 2 arcs');
  67811. $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 2 arcs');
  67812. }
  67813. function test_undirected_degree() {
  67814. $this->_graph = new Structures_Graph(false);
  67815. $node = array();
  67816. $node[] = new Structures_Graph_Node();
  67817. $node[] = new Structures_Graph_Node();
  67818. $node[] = new Structures_Graph_Node();
  67819. $this->_graph->addNode($node[0]);
  67820. $this->_graph->addNode($node[1]);
  67821. $this->_graph->addNode($node[2]);
  67822. $this->assertEquals(0, $node[0]->inDegree(), 'inDegree test failed for node 0 with 0 arcs');
  67823. $this->assertEquals(0, $node[1]->inDegree(), 'inDegree test failed for node 1 with 0 arcs');
  67824. $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 0 arcs');
  67825. $this->assertEquals(0, $node[0]->outDegree(), 'outDegree test failed for node 0 with 0 arcs');
  67826. $this->assertEquals(0, $node[1]->outDegree(), 'outDegree test failed for node 1 with 0 arcs');
  67827. $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 0 arcs');
  67828. $node[0]->connectTo($node[1]);
  67829. $this->assertEquals(1, $node[0]->inDegree(), 'inDegree test failed for node 0 with 1 arc');
  67830. $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 1 arc');
  67831. $this->assertEquals(0, $node[2]->inDegree(), 'inDegree test failed for node 2 with 1 arc');
  67832. $this->assertEquals(1, $node[0]->outDegree(), 'outDegree test failed for node 0 with 1 arc');
  67833. $this->assertEquals(1, $node[1]->outDegree(), 'outDegree test failed for node 1 with 1 arc');
  67834. $this->assertEquals(0, $node[2]->outDegree(), 'outDegree test failed for node 2 with 1 arc');
  67835. $node[0]->connectTo($node[2]);
  67836. $this->assertEquals(2, $node[0]->inDegree(), 'inDegree test failed for node 0 with 2 arcs');
  67837. $this->assertEquals(1, $node[1]->inDegree(), 'inDegree test failed for node 1 with 2 arcs');
  67838. $this->assertEquals(1, $node[2]->inDegree(), 'inDegree test failed for node 2 with 2 arcs');
  67839. $this->assertEquals(2, $node[0]->outDegree(), 'outDegree test failed for node 0 with 2 arcs');
  67840. $this->assertEquals(1, $node[1]->outDegree(), 'outDegree test failed for node 1 with 2 arcs');
  67841. $this->assertEquals(1, $node[2]->outDegree(), 'outDegree test failed for node 2 with 2 arcs');
  67842. }
  67843. }
  67844. ?>
  67845. ���������������������������������������������������������������������������������������������������������������������������Structures_Graph-1.2.0/tests/helper.inc�������������������������������������������������������������0000644�0001750�0001750�00000000525�14627105564�020016� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  67846. if ('@php_dir@' == '@'.'php_dir'.'@') {
  67847. // This package hasn't been installed.
  67848. // Adjust path to ensure includes find files in working directory.
  67849. set_include_path(dirname(dirname(__FILE__))
  67850. . PATH_SEPARATOR . dirname(__FILE__)
  67851. . PATH_SEPARATOR . get_include_path());
  67852. }
  67853. require_once 'Structures/Graph.php';
  67854. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������Structures_Graph-1.2.0/LICENSE����������������������������������������������������������������������0000644�0001750�0001750�00000016725�14627105564�015720� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GNU LESSER GENERAL PUBLIC LICENSE
  67855. Version 3, 29 June 2007
  67856. Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
  67857. Everyone is permitted to copy and distribute verbatim copies
  67858. of this license document, but changing it is not allowed.
  67859. This version of the GNU Lesser General Public License incorporates
  67860. the terms and conditions of version 3 of the GNU General Public
  67861. License, supplemented by the additional permissions listed below.
  67862. 0. Additional Definitions.
  67863. As used herein, "this License" refers to version 3 of the GNU Lesser
  67864. General Public License, and the "GNU GPL" refers to version 3 of the GNU
  67865. General Public License.
  67866. "The Library" refers to a covered work governed by this License,
  67867. other than an Application or a Combined Work as defined below.
  67868. An "Application" is any work that makes use of an interface provided
  67869. by the Library, but which is not otherwise based on the Library.
  67870. Defining a subclass of a class defined by the Library is deemed a mode
  67871. of using an interface provided by the Library.
  67872. A "Combined Work" is a work produced by combining or linking an
  67873. Application with the Library. The particular version of the Library
  67874. with which the Combined Work was made is also called the "Linked
  67875. Version".
  67876. The "Minimal Corresponding Source" for a Combined Work means the
  67877. Corresponding Source for the Combined Work, excluding any source code
  67878. for portions of the Combined Work that, considered in isolation, are
  67879. based on the Application, and not on the Linked Version.
  67880. The "Corresponding Application Code" for a Combined Work means the
  67881. object code and/or source code for the Application, including any data
  67882. and utility programs needed for reproducing the Combined Work from the
  67883. Application, but excluding the System Libraries of the Combined Work.
  67884. 1. Exception to Section 3 of the GNU GPL.
  67885. You may convey a covered work under sections 3 and 4 of this License
  67886. without being bound by section 3 of the GNU GPL.
  67887. 2. Conveying Modified Versions.
  67888. If you modify a copy of the Library, and, in your modifications, a
  67889. facility refers to a function or data to be supplied by an Application
  67890. that uses the facility (other than as an argument passed when the
  67891. facility is invoked), then you may convey a copy of the modified
  67892. version:
  67893. a) under this License, provided that you make a good faith effort to
  67894. ensure that, in the event an Application does not supply the
  67895. function or data, the facility still operates, and performs
  67896. whatever part of its purpose remains meaningful, or
  67897. b) under the GNU GPL, with none of the additional permissions of
  67898. this License applicable to that copy.
  67899. 3. Object Code Incorporating Material from Library Header Files.
  67900. The object code form of an Application may incorporate material from
  67901. a header file that is part of the Library. You may convey such object
  67902. code under terms of your choice, provided that, if the incorporated
  67903. material is not limited to numerical parameters, data structure
  67904. layouts and accessors, or small macros, inline functions and templates
  67905. (ten or fewer lines in length), you do both of the following:
  67906. a) Give prominent notice with each copy of the object code that the
  67907. Library is used in it and that the Library and its use are
  67908. covered by this License.
  67909. b) Accompany the object code with a copy of the GNU GPL and this license
  67910. document.
  67911. 4. Combined Works.
  67912. You may convey a Combined Work under terms of your choice that,
  67913. taken together, effectively do not restrict modification of the
  67914. portions of the Library contained in the Combined Work and reverse
  67915. engineering for debugging such modifications, if you also do each of
  67916. the following:
  67917. a) Give prominent notice with each copy of the Combined Work that
  67918. the Library is used in it and that the Library and its use are
  67919. covered by this License.
  67920. b) Accompany the Combined Work with a copy of the GNU GPL and this license
  67921. document.
  67922. c) For a Combined Work that displays copyright notices during
  67923. execution, include the copyright notice for the Library among
  67924. these notices, as well as a reference directing the user to the
  67925. copies of the GNU GPL and this license document.
  67926. d) Do one of the following:
  67927. 0) Convey the Minimal Corresponding Source under the terms of this
  67928. License, and the Corresponding Application Code in a form
  67929. suitable for, and under terms that permit, the user to
  67930. recombine or relink the Application with a modified version of
  67931. the Linked Version to produce a modified Combined Work, in the
  67932. manner specified by section 6 of the GNU GPL for conveying
  67933. Corresponding Source.
  67934. 1) Use a suitable shared library mechanism for linking with the
  67935. Library. A suitable mechanism is one that (a) uses at run time
  67936. a copy of the Library already present on the user's computer
  67937. system, and (b) will operate properly with a modified version
  67938. of the Library that is interface-compatible with the Linked
  67939. Version.
  67940. e) Provide Installation Information, but only if you would otherwise
  67941. be required to provide such information under section 6 of the
  67942. GNU GPL, and only to the extent that such information is
  67943. necessary to install and execute a modified version of the
  67944. Combined Work produced by recombining or relinking the
  67945. Application with a modified version of the Linked Version. (If
  67946. you use option 4d0, the Installation Information must accompany
  67947. the Minimal Corresponding Source and Corresponding Application
  67948. Code. If you use option 4d1, you must provide the Installation
  67949. Information in the manner specified by section 6 of the GNU GPL
  67950. for conveying Corresponding Source.)
  67951. 5. Combined Libraries.
  67952. You may place library facilities that are a work based on the
  67953. Library side by side in a single library together with other library
  67954. facilities that are not Applications and are not covered by this
  67955. License, and convey such a combined library under terms of your
  67956. choice, if you do both of the following:
  67957. a) Accompany the combined library with a copy of the same work based
  67958. on the Library, uncombined with any other library facilities,
  67959. conveyed under the terms of this License.
  67960. b) Give prominent notice with the combined library that part of it
  67961. is a work based on the Library, and explaining where to find the
  67962. accompanying uncombined form of the same work.
  67963. 6. Revised Versions of the GNU Lesser General Public License.
  67964. The Free Software Foundation may publish revised and/or new versions
  67965. of the GNU Lesser General Public License from time to time. Such new
  67966. versions will be similar in spirit to the present version, but may
  67967. differ in detail to address new problems or concerns.
  67968. Each version is given a distinguishing version number. If the
  67969. Library as you received it specifies that a certain numbered version
  67970. of the GNU Lesser General Public License "or any later version"
  67971. applies to it, you have the option of following the terms and
  67972. conditions either of that published version or of any later version
  67973. published by the Free Software Foundation. If the Library as you
  67974. received it does not specify a version number of the GNU Lesser
  67975. General Public License, you may choose any version of the GNU Lesser
  67976. General Public License ever published by the Free Software Foundation.
  67977. If the Library as you received it specifies that a proxy can decide
  67978. whether future versions of the GNU Lesser General Public License shall
  67979. apply, that proxy's public statement of acceptance of any version is
  67980. permanent authorization for you to choose that version for the
  67981. Library.
  67982. �������������������������������������������package.sig�����������������������������������������������������������������������������������������0000664�0001750�0001750�00000000303�14627105631�013030� 0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������-----BEGIN PGP SIGNATURE-----
  67983. iF0EABECAB0WIQQQ9oz3P4qkJvYXdSlyoyG6wkXxdQUCZlyLmQAKCRByoyG6wkXx
  67984. ddmsAKDXqUAP3XSe+KxF50U3ijsd3kGCrwCfcXbBiAWmeuYF++XeN8S3BeA4+Vc=
  67985. =UcVl
  67986. -----END PGP SIGNATURE-----
  67987. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������package.xml�����������������������������������������������������������������������������������������0000644�0001750�0001750�00000045621�13647063022�013056 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?>
  67988. <package packagerversion="1.10.5" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
  67989. <name>XML_Util</name>
  67990. <channel>pear.php.net</channel>
  67991. <summary>XML utility class</summary>
  67992. <description>Selection of methods that are often needed when working with XML documents. Functionality includes creating of attribute lists from arrays, creation of tags, validation of XML names and more.</description>
  67993. <lead>
  67994. <name>Chuck Burgess</name>
  67995. <user>ashnazg</user>
  67996. <email>ashnazg@php.net</email>
  67997. <active>yes</active>
  67998. </lead>
  67999. <lead>
  68000. <name>Stephan Schmidt</name>
  68001. <user>schst</user>
  68002. <email>schst@php-tools.net</email>
  68003. <active>no</active>
  68004. </lead>
  68005. <helper>
  68006. <name>Davey Shafik</name>
  68007. <user>davey</user>
  68008. <email>davey@php.net</email>
  68009. <active>no</active>
  68010. </helper>
  68011. <date>2020-04-19</date>
  68012. <time>14:54:10</time>
  68013. <version>
  68014. <release>1.4.5</release>
  68015. <api>1.4.0</api>
  68016. </version>
  68017. <stability>
  68018. <release>stable</release>
  68019. <api>stable</api>
  68020. </stability>
  68021. <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
  68022. <notes>
  68023. * PR #12: fix Trying to access array offset on value of type int
  68024. </notes>
  68025. <contents>
  68026. <dir baseinstalldir="/" name="/">
  68027. <file baseinstalldir="/" md5sum="af2746028ae4395f549855a5e444ada7" name="examples/example.php" role="doc" />
  68028. <file baseinstalldir="/" md5sum="b9e52f4aa372c4067c609f49c2285b8f" name="examples/example2.php" role="doc" />
  68029. <file baseinstalldir="/" md5sum="d0af9354df0962e70e9e2215b5611b9c" name="tests/AbstractUnitTests.php" role="test" />
  68030. <file baseinstalldir="/" md5sum="57ce547d64d6e1f2986c313407deffef" name="tests/ApiVersionTests.php" role="test" />
  68031. <file baseinstalldir="/" md5sum="2d0427db94790df7ada24a744547edf5" name="tests/AttributesToStringTests.php" role="test" />
  68032. <file baseinstalldir="/" md5sum="673d1438c4718a70c5da3fe019027db4" name="tests/CollapseEmptyTagsTests.php" role="test" />
  68033. <file baseinstalldir="/" md5sum="46b981f91edd163f1cd021cfef5d1bb1" name="tests/CreateCDataSectionTests.php" role="test" />
  68034. <file baseinstalldir="/" md5sum="6aa925b879572e9b3f1885b7cdbb223b" name="tests/CreateCommentTests.php" role="test" />
  68035. <file baseinstalldir="/" md5sum="dbc083b62a020fa245fde5a7828a4806" name="tests/CreateEndElementTests.php" role="test" />
  68036. <file baseinstalldir="/" md5sum="f58e38343ecf60811c842d4cfc8194ae" name="tests/CreateStartElementTests.php" role="test" />
  68037. <file baseinstalldir="/" md5sum="9385fba272f4ebccf4c95d43d16dcff4" name="tests/CreateTagTests.php" role="test" />
  68038. <file baseinstalldir="/" md5sum="51e7ba1390e6dadc3c0be0c960bf171d" name="tests/CreateTagFromArrayTests.php" role="test" />
  68039. <file baseinstalldir="/" md5sum="6bbb54ef4cf56dc2c0b558b295de5668" name="tests/GetDocTypeDeclarationTests.php" role="test" />
  68040. <file baseinstalldir="/" md5sum="825b440b0ee8abd10b4df017c08bf15f" name="tests/GetXmlDeclarationTests.php" role="test" />
  68041. <file baseinstalldir="/" md5sum="e6783bb330f8f2ae7225f02d56f194e4" name="tests/IsValidNameTests.php" role="test" />
  68042. <file baseinstalldir="/" md5sum="b273525b905ae6d5fc53adcb3ce0b8d9" name="tests/RaiseErrorTests.php" role="test" />
  68043. <file baseinstalldir="/" md5sum="20befbef5e55639539336761a17c64f3" name="tests/ReplaceEntitiesTests.php" role="test" />
  68044. <file baseinstalldir="/" md5sum="a3ceff3302e31f90130be01c312b33b3" name="tests/ReverseEntitiesTests.php" role="test" />
  68045. <file baseinstalldir="/" md5sum="aeb95108896180ef77a7dce3c310a3b8" name="tests/SplitQualifiedNameTests.php" role="test" />
  68046. <file baseinstalldir="/" md5sum="e93010b1eff68f889fefcb006bf20b63" name="tests/Bug4950Tests.php" role="test" />
  68047. <file baseinstalldir="/" md5sum="748ffb640e13e7b960385c7e12413782" name="tests/Bug5392Tests.php" role="test" />
  68048. <file baseinstalldir="/" md5sum="01e68b66e27a6fdb197d572c67ae6bc5" name="tests/Bug18343Tests.php" role="test" />
  68049. <file baseinstalldir="/" md5sum="d945220c38344bc773b18244439bb0cc" name="tests/Bug21177Tests.php" role="test" />
  68050. <file baseinstalldir="/" md5sum="af2672bb90875c2e00f93f563bfafe70" name="tests/Bug21184Tests.php" role="test" />
  68051. <file baseinstalldir="/" md5sum="0db6fa9c169bf6904aa7e588c2325a13" name="XML/Util.php" role="php">
  68052. <tasks:replace from="@version@" to="version" type="package-info" />
  68053. </file>
  68054. </dir>
  68055. </contents>
  68056. <dependencies>
  68057. <required>
  68058. <php>
  68059. <min>5.4.0</min>
  68060. </php>
  68061. <pearinstaller>
  68062. <min>1.9.0</min>
  68063. </pearinstaller>
  68064. <extension>
  68065. <name>pcre</name>
  68066. </extension>
  68067. </required>
  68068. </dependencies>
  68069. <phprelease />
  68070. <changelog>
  68071. <release>
  68072. <version>
  68073. <release>0.1</release>
  68074. <api>0.1</api>
  68075. </version>
  68076. <stability>
  68077. <release>stable</release>
  68078. <api>stable</api>
  68079. </stability>
  68080. <date>2003-08-01</date>
  68081. <license uri="http://www.php.net/license">PHP License</license>
  68082. <notes>
  68083. inital release
  68084. </notes>
  68085. </release>
  68086. <release>
  68087. <version>
  68088. <release>0.1.1</release>
  68089. <api>0.1.1</api>
  68090. </version>
  68091. <stability>
  68092. <release>stable</release>
  68093. <api>stable</api>
  68094. </stability>
  68095. <date>2003-08-02</date>
  68096. <license uri="http://www.php.net/license">PHP License</license>
  68097. <notes>
  68098. bugfix: removed bug in createTagFromArray
  68099. </notes>
  68100. </release>
  68101. <release>
  68102. <version>
  68103. <release>0.2</release>
  68104. <api>0.2</api>
  68105. </version>
  68106. <stability>
  68107. <release>stable</release>
  68108. <api>stable</api>
  68109. </stability>
  68110. <date>2003-08-12</date>
  68111. <license uri="http://www.php.net/license">PHP License</license>
  68112. <notes>
  68113. added XML_Util::getDocTypeDeclaration()
  68114. </notes>
  68115. </release>
  68116. <release>
  68117. <version>
  68118. <release>0.2.1</release>
  68119. <api>0.2.1</api>
  68120. </version>
  68121. <stability>
  68122. <release>stable</release>
  68123. <api>stable</api>
  68124. </stability>
  68125. <date>2003-09-05</date>
  68126. <license uri="http://www.php.net/license">PHP License</license>
  68127. <notes>
  68128. fixed bug with zero as tag content in createTagFromArray and createTag
  68129. </notes>
  68130. </release>
  68131. <release>
  68132. <version>
  68133. <release>0.3</release>
  68134. <api>0.3</api>
  68135. </version>
  68136. <stability>
  68137. <release>stable</release>
  68138. <api>stable</api>
  68139. </stability>
  68140. <date>2003-09-12</date>
  68141. <license uri="http://www.php.net/license">PHP License</license>
  68142. <notes>
  68143. added createStartElement() and createEndElement()
  68144. </notes>
  68145. </release>
  68146. <release>
  68147. <version>
  68148. <release>0.4</release>
  68149. <api>0.4</api>
  68150. </version>
  68151. <stability>
  68152. <release>stable</release>
  68153. <api>stable</api>
  68154. </stability>
  68155. <date>2003-09-21</date>
  68156. <license uri="http://www.php.net/license">PHP License</license>
  68157. <notes>
  68158. added createCDataSection(),
  68159. added support for CData sections in createTag* methods,
  68160. fixed bug #23,
  68161. fixed bug in splitQualifiedName()
  68162. </notes>
  68163. </release>
  68164. <release>
  68165. <version>
  68166. <release>0.5</release>
  68167. <api>0.5</api>
  68168. </version>
  68169. <stability>
  68170. <release>stable</release>
  68171. <api>stable</api>
  68172. </stability>
  68173. <date>2003-09-23</date>
  68174. <license uri="http://www.php.net/license">PHP License</license>
  68175. <notes>
  68176. added support for multiline attributes in attributesToString(), createTag*() and createStartElement (requested by Yavor Shahpasov for XML_Serializer),
  68177. added createComment
  68178. </notes>
  68179. </release>
  68180. <release>
  68181. <version>
  68182. <release>0.5.1</release>
  68183. <api>0.5.1</api>
  68184. </version>
  68185. <stability>
  68186. <release>stable</release>
  68187. <api>stable</api>
  68188. </stability>
  68189. <date>2003-09-26</date>
  68190. <license uri="http://www.php.net/license">PHP License</license>
  68191. <notes>
  68192. added default namespace parameter (optional) in splitQualifiedName() (requested by Sebastian Bergmann)
  68193. </notes>
  68194. </release>
  68195. <release>
  68196. <version>
  68197. <release>0.5.2</release>
  68198. <api>0.5.2</api>
  68199. </version>
  68200. <stability>
  68201. <release>stable</release>
  68202. <api>stable</api>
  68203. </stability>
  68204. <date>2003-11-22</date>
  68205. <license uri="http://www.php.net/license">PHP License</license>
  68206. <notes>
  68207. now creates XHTML compliant empty tags (Davey),
  68208. minor whitespace fixes (Davey)
  68209. </notes>
  68210. </release>
  68211. <release>
  68212. <version>
  68213. <release>0.6.0beta1</release>
  68214. <api>0.6.0beta1</api>
  68215. </version>
  68216. <stability>
  68217. <release>beta</release>
  68218. <api>beta</api>
  68219. </stability>
  68220. <date>2004-05-24</date>
  68221. <license uri="http://www.php.net/license">PHP License</license>
  68222. <notes>
  68223. - Fixed bug 1438 (namespaces not accepted for isValidName()) (thanks to davey)
  68224. - added optional parameter to replaceEntities() to define the set of entities to replace
  68225. - added optional parameter to attributesToString() to define, whether entities should be replaced (requested by Sebastian Bergmann)
  68226. - allowed second parameter to XML_Util::attributesToString() to be an array containing options (easier to use, if you only need to set the last parameter)
  68227. - introduced XML_Util::raiseError() to avoid the necessity of including PEAR.php, will only be included on error
  68228. </notes>
  68229. </release>
  68230. <release>
  68231. <version>
  68232. <release>0.6.0</release>
  68233. <api>0.6.0</api>
  68234. </version>
  68235. <stability>
  68236. <release>stable</release>
  68237. <api>stable</api>
  68238. </stability>
  68239. <date>2004-06-07</date>
  68240. <license uri="http://www.php.net/license">PHP License</license>
  68241. <notes>
  68242. - Fixed bug 1438 (namespaces not accepted for isValidName()) (thanks to davey)
  68243. - added optional parameter to replaceEntities() to define the set of entities to replace
  68244. - added optional parameter to attributesToString() to define, whether entities should be replaced (requested by Sebastian Bergmann)
  68245. - allowed second parameter to XML_Util::attributesToString() to be an array containing options (easier to use, if you only need to set the last parameter)
  68246. - introduced XML_Util::raiseError() to avoid the necessity of including PEAR.php, will only be included on error
  68247. </notes>
  68248. </release>
  68249. <release>
  68250. <version>
  68251. <release>0.6.1</release>
  68252. <api>0.6.1</api>
  68253. </version>
  68254. <stability>
  68255. <release>stable</release>
  68256. <api>stable</api>
  68257. </stability>
  68258. <date>2004-10-28</date>
  68259. <license uri="http://www.php.net/license">PHP License</license>
  68260. <notes>
  68261. - Added check for tag name (either as local part or qualified name) in createTagFromArray() (bug #1083)
  68262. </notes>
  68263. </release>
  68264. <release>
  68265. <version>
  68266. <release>1.0.0</release>
  68267. <api>1.0.0</api>
  68268. </version>
  68269. <stability>
  68270. <release>stable</release>
  68271. <api>stable</api>
  68272. </stability>
  68273. <date>2004-10-28</date>
  68274. <license uri="http://www.php.net/license">PHP License</license>
  68275. <notes>
  68276. - Added reverseEntities() (request #2639)
  68277. </notes>
  68278. </release>
  68279. <release>
  68280. <version>
  68281. <release>1.1.0</release>
  68282. <api>1.1.0</api>
  68283. </version>
  68284. <stability>
  68285. <release>stable</release>
  68286. <api>stable</api>
  68287. </stability>
  68288. <date>2004-11-19</date>
  68289. <license uri="http://www.php.net/license">PHP License</license>
  68290. <notes>
  68291. - Added collapseEmptyTags (patch by Sebastian Bergmann and Thomas Duffey)
  68292. </notes>
  68293. </release>
  68294. <release>
  68295. <version>
  68296. <release>1.1.1</release>
  68297. <api>1.1.1</api>
  68298. </version>
  68299. <stability>
  68300. <release>stable</release>
  68301. <api>stable</api>
  68302. </stability>
  68303. <date>2004-12-23</date>
  68304. <license uri="http://www.php.net/license">PHP License</license>
  68305. <notes>
  68306. - fixed bug in replaceEntities() and reverseEntities() in conjunction with XML_UTIL_ENTITIES_HTML
  68307. - createTag() and createTagFromArray() now accept XML_UTIL_ENTITIES_XML, XML_UTIL_ENTITIES_XML_REQUIRED, XML_UTIL_ENTITIES_HTML, XML_UTIL_ENTITIES_NONE and XML_UTIL_CDATA_SECTION as $replaceEntities parameter
  68308. </notes>
  68309. </release>
  68310. <release>
  68311. <version>
  68312. <release>1.1.2</release>
  68313. <api>1.1.2</api>
  68314. </version>
  68315. <stability>
  68316. <release>stable</release>
  68317. <api>stable</api>
  68318. </stability>
  68319. <date>2006-12-01</date>
  68320. <license uri="http://www.php.net/license">PHP License</license>
  68321. <notes>
  68322. - fixed bug #5419: isValidName() now checks for character classes
  68323. - implemented request #8196: added optional parameter to influence array sorting to createTag() createTagFromArray() and createStartElement()
  68324. </notes>
  68325. </release>
  68326. <release>
  68327. <version>
  68328. <release>1.1.4</release>
  68329. <api>1.1.4</api>
  68330. </version>
  68331. <stability>
  68332. <release>stable</release>
  68333. <api>stable</api>
  68334. </stability>
  68335. <date>2006-12-16</date>
  68336. <license uri="http://www.php.net/license">PHP License</license>
  68337. <notes>
  68338. - Fixed bug #9561: Not allowing underscores in middle of tags
  68339. </notes>
  68340. </release>
  68341. <release>
  68342. <version>
  68343. <release>1.2.0a1</release>
  68344. <api>1.2.0</api>
  68345. </version>
  68346. <stability>
  68347. <release>alpha</release>
  68348. <api>alpha</api>
  68349. </stability>
  68350. <date>2008-05-04</date>
  68351. <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
  68352. <notes>
  68353. Changed license to New BSD License (Req #13826 [ashnazg])
  68354. Added a test suite against all API methods [ashnazg]
  68355. Switch to package.xml v2 [ashnazg]
  68356. Fixed Bug #4950: Incorrect CDATA serializing [ashnazg|ja.doma]
  68357. </notes>
  68358. </release>
  68359. <release>
  68360. <version>
  68361. <release>1.2.0a2</release>
  68362. <api>1.2.0</api>
  68363. </version>
  68364. <stability>
  68365. <release>alpha</release>
  68366. <api>alpha</api>
  68367. </stability>
  68368. <date>2008-05-22</date>
  68369. <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
  68370. <notes>
  68371. Changed license to New BSD License (Req #13826 [ashnazg])
  68372. Added a test suite against all API methods [ashnazg]
  68373. Switch to package.xml v2 [ashnazg]
  68374. Added Req #13839: Missing XHTML empty tags to collapse [ashnazg|drry]
  68375. Fixed Bug #5392: encoding of ISO-8859-1 is the only supported encoding [ashnazg]
  68376. Fixed Bug #4950: Incorrect CDATA serializing [ashnazg|drry]
  68377. -- (this fix differs from the one in v1.2.0a1)
  68378. </notes>
  68379. </release>
  68380. <release>
  68381. <version>
  68382. <release>1.2.0RC1</release>
  68383. <api>1.2.0</api>
  68384. </version>
  68385. <stability>
  68386. <release>beta</release>
  68387. <api>beta</api>
  68388. </stability>
  68389. <date>2008-07-12</date>
  68390. <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
  68391. <notes>
  68392. Changed license to New BSD License (Req #13826 [ashnazg])
  68393. Added a test suite against all API methods [ashnazg]
  68394. Switch to package.xml v2 [ashnazg]
  68395. Added Req #13839: Missing XHTML empty tags to collapse [ashnazg|drry]
  68396. Fixed Bug #5392: encoding of ISO-8859-1 is the only supported encoding [ashnazg]
  68397. Fixed Bug #4950: Incorrect CDATA serializing [ashnazg|drry]
  68398. -- (this fix differs from the one in v1.2.0a1)
  68399. </notes>
  68400. </release>
  68401. <release>
  68402. <version>
  68403. <release>1.2.0</release>
  68404. <api>1.2.0</api>
  68405. </version>
  68406. <stability>
  68407. <release>stable</release>
  68408. <api>stable</api>
  68409. </stability>
  68410. <date>2008-07-26</date>
  68411. <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
  68412. <notes>
  68413. Changed license to New BSD License (Req #13826 [ashnazg])
  68414. Added a test suite against all API methods [ashnazg]
  68415. Switch to package.xml v2 [ashnazg]
  68416. Added Req #13839: Missing XHTML empty tags to collapse [ashnazg|drry]
  68417. Fixed Bug #5392: encoding of ISO-8859-1 is the only supported encoding [ashnazg]
  68418. Fixed Bug #4950: Incorrect CDATA serializing [ashnazg|drry]
  68419. -- (this fix differs from the one in v1.2.0a1)
  68420. </notes>
  68421. </release>
  68422. <release>
  68423. <version>
  68424. <release>1.2.1</release>
  68425. <api>1.2.0</api>
  68426. </version>
  68427. <stability>
  68428. <release>stable</release>
  68429. <api>stable</api>
  68430. </stability>
  68431. <date>2011-12-31</date>
  68432. <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
  68433. <notes>
  68434. Fixed Bug #14760: Bug in getDocTypeDeclaration() [ashnazg|fpospisil]
  68435. </notes>
  68436. </release>
  68437. <release>
  68438. <version>
  68439. <release>1.2.2</release>
  68440. <api>1.2.0</api>
  68441. </version>
  68442. <stability>
  68443. <release>stable</release>
  68444. <api>stable</api>
  68445. </stability>
  68446. <date>2014-06-07</date>
  68447. <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
  68448. <notes>
  68449. QA release
  68450. Bug #18343 Entities in file names decoded during packaging
  68451. Bug #19174 upgrade PHPUnit require statements &amp; other fixes (for PEAR QA Team)
  68452. Request #19750 examples/example.php encoding
  68453. </notes>
  68454. </release>
  68455. <release>
  68456. <version>
  68457. <release>1.2.3</release>
  68458. <api>1.2.0</api>
  68459. </version>
  68460. <stability>
  68461. <release>stable</release>
  68462. <api>stable</api>
  68463. </stability>
  68464. <date>2014-06-07</date>
  68465. <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
  68466. <notes>
  68467. Bug #20293 Broken installation for 1.2.2
  68468. </notes>
  68469. </release>
  68470. <release>
  68471. <version>
  68472. <release>1.3.0</release>
  68473. <api>1.3.0</api>
  68474. </version>
  68475. <stability>
  68476. <release>stable</release>
  68477. <api>stable</api>
  68478. </stability>
  68479. <date>2015-02-27</date>
  68480. <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
  68481. <notes>
  68482. * Set minimum PHP version to 5.3.0
  68483. * Mark static methods with static keyword
  68484. </notes>
  68485. </release>
  68486. <release>
  68487. <version>
  68488. <release>1.4.0</release>
  68489. <api>1.4.0</api>
  68490. </version>
  68491. <stability>
  68492. <release>stable</release>
  68493. <api>stable</api>
  68494. </stability>
  68495. <date>2017-02-03</date>
  68496. <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
  68497. <notes>
  68498. * Set minimum PHP version to 5.4.0
  68499. * Set minimum PEAR version to 1.10.1
  68500. * Adds a new XML_UTIL_COLLAPSE_NONE option
  68501. for preventing empty tag collapsing.
  68502. * Request #15467 CDATA sections and blank nodes
  68503. </notes>
  68504. </release>
  68505. <release>
  68506. <version>
  68507. <release>1.4.1</release>
  68508. <api>1.4.0</api>
  68509. </version>
  68510. <stability>
  68511. <release>stable</release>
  68512. <api>stable</api>
  68513. </stability>
  68514. <date>2017-02-07</date>
  68515. <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
  68516. <notes>
  68517. * Bug #21177 XML_Util::collapseEmptyTags() can return NULL
  68518. </notes>
  68519. </release>
  68520. <release>
  68521. <version>
  68522. <release>1.4.2</release>
  68523. <api>1.4.0</api>
  68524. </version>
  68525. <stability>
  68526. <release>stable</release>
  68527. <api>stable</api>
  68528. </stability>
  68529. <date>2017-02-22</date>
  68530. <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
  68531. <notes>
  68532. * Bug #21184 Collapse issue
  68533. </notes>
  68534. </release>
  68535. <release>
  68536. <version>
  68537. <release>1.4.3</release>
  68538. <api>1.4.0</api>
  68539. </version>
  68540. <stability>
  68541. <release>stable</release>
  68542. <api>stable</api>
  68543. </stability>
  68544. <date>2017-06-28</date>
  68545. <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
  68546. <notes>
  68547. * Decrease minimum PEAR version to 1.9.0 to allow PEAR upgrades
  68548. </notes>
  68549. </release>
  68550. <release>
  68551. <version>
  68552. <release>1.4.4</release>
  68553. <api>1.4.0</api>
  68554. </version>
  68555. <stability>
  68556. <release>stable</release>
  68557. <api>stable</api>
  68558. </stability>
  68559. <date>2019-12-05</date>
  68560. <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
  68561. <notes>
  68562. * PR #11: fix phplint warning
  68563. </notes>
  68564. </release>
  68565. <release>
  68566. <version>
  68567. <release>1.4.5</release>
  68568. <api>1.4.0</api>
  68569. </version>
  68570. <stability>
  68571. <release>stable</release>
  68572. <api>stable</api>
  68573. </stability>
  68574. <date>2020-04-19</date>
  68575. <license uri="http://opensource.org/licenses/bsd-license">BSD License</license>
  68576. <notes>
  68577. * PR #12: fix Trying to access array offset on value of type int
  68578. </notes>
  68579. </release>
  68580. </changelog>
  68581. </package>
  68582. ���������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/examples/example.php�����������������������������������������������������������������0000644�0001750�0001750�00000021710�13647063022�017014 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  68583. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  68584. /**
  68585. * Examples (file #1)
  68586. *
  68587. * several examples for the methods of XML_Util
  68588. *
  68589. * PHP versions 4 and 5
  68590. *
  68591. * LICENSE:
  68592. *
  68593. * Copyright (c) 2003-2008 Stephan Schmidt <schst@php.net>
  68594. * All rights reserved.
  68595. *
  68596. * Redistribution and use in source and binary forms, with or without
  68597. * modification, are permitted provided that the following conditions
  68598. * are met:
  68599. *
  68600. * * Redistributions of source code must retain the above copyright
  68601. * notice, this list of conditions and the following disclaimer.
  68602. * * Redistributions in binary form must reproduce the above copyright
  68603. * notice, this list of conditions and the following disclaimer in the
  68604. * documentation and/or other materials provided with the distribution.
  68605. * * The name of the author may not be used to endorse or promote products
  68606. * derived from this software without specific prior written permission.
  68607. *
  68608. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  68609. * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  68610. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  68611. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  68612. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  68613. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  68614. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  68615. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  68616. * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  68617. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  68618. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  68619. *
  68620. * @category XML
  68621. * @package XML_Util
  68622. * @subpackage Examples
  68623. * @author Stephan Schmidt <schst@php.net>
  68624. * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
  68625. * @license http://opensource.org/licenses/bsd-license New BSD License
  68626. * @version CVS: $Id$
  68627. * @link http://pear.php.net/package/XML_Util
  68628. */
  68629. /**
  68630. * set error level
  68631. */
  68632. error_reporting(E_ALL);
  68633. require_once 'XML/Util.php';
  68634. /**
  68635. * replacing XML entities
  68636. */
  68637. print 'replace XML entities:<br>';
  68638. print XML_Util::replaceEntities('This string contains < & >.');
  68639. print "\n<br><br>\n";
  68640. /**
  68641. * reversing XML entities
  68642. */
  68643. print 'replace XML entities:<br>';
  68644. print XML_Util::reverseEntities('This string contains &lt; &amp; &gt;.');
  68645. print "\n<br><br>\n";
  68646. /**
  68647. * building XML declaration
  68648. */
  68649. print 'building XML declaration:<br>';
  68650. print htmlspecialchars(XML_Util::getXMLDeclaration());
  68651. print "\n<br><br>\n";
  68652. print 'building XML declaration with additional attributes:<br>';
  68653. print htmlspecialchars(XML_Util::getXMLDeclaration('1.0', 'UTF-8', true));
  68654. print "\n<br><br>\n";
  68655. /**
  68656. * building document type declaration
  68657. */
  68658. print 'building DocType declaration:<br>';
  68659. print htmlspecialchars(XML_Util::getDocTypeDeclaration('package',
  68660. 'http://pear.php.net/dtd/package-1.0'));
  68661. print "\n<br><br>\n";
  68662. print 'building DocType declaration with public ID (does not exist):<br>';
  68663. print htmlspecialchars(XML_Util::getDocTypeDeclaration('package',
  68664. array('uri' => 'http://pear.php.net/dtd/package-1.0',
  68665. 'id' => '-//PHP//PEAR/DTD PACKAGE 0.1')));
  68666. print "\n<br><br>\n";
  68667. print 'building DocType declaration with internal DTD:<br>';
  68668. print '<pre>';
  68669. print htmlspecialchars(XML_Util::getDocTypeDeclaration('package',
  68670. 'http://pear.php.net/dtd/package-1.0',
  68671. '<!ELEMENT additionalInfo (#PCDATA)>'));
  68672. print '</pre>';
  68673. print "\n<br><br>\n";
  68674. /**
  68675. * creating an attribute string
  68676. */
  68677. $att = array(
  68678. 'foo' => 'bar',
  68679. 'argh' => 'tomato'
  68680. );
  68681. print 'converting array to string:<br>';
  68682. print XML_Util::attributesToString($att);
  68683. print "\n<br><br>\n";
  68684. /**
  68685. * creating an attribute string with linebreaks
  68686. */
  68687. $att = array(
  68688. 'foo' => 'bar',
  68689. 'argh' => 'tomato'
  68690. );
  68691. print 'converting array to string (including line breaks):<br>';
  68692. print '<pre>';
  68693. print XML_Util::attributesToString($att, true, true);
  68694. print '</pre>';
  68695. print "\n<br><br>\n";
  68696. /**
  68697. * splitting a qualified tag name
  68698. */
  68699. print 'splitting qualified tag name:<br>';
  68700. print '<pre>';
  68701. print_r(XML_Util::splitQualifiedName('xslt:stylesheet'));
  68702. print '</pre>';
  68703. print "\n<br>\n";
  68704. /**
  68705. * splitting a qualified tag name (no namespace)
  68706. */
  68707. print 'splitting qualified tag name (no namespace):<br>';
  68708. print '<pre>';
  68709. print_r(XML_Util::splitQualifiedName('foo'));
  68710. print '</pre>';
  68711. print "\n<br>\n";
  68712. /**
  68713. * splitting a qualified tag name (no namespace, but default namespace specified)
  68714. */
  68715. print 'splitting qualified tag name '
  68716. . '(no namespace, but default namespace specified):<br>';
  68717. print '<pre>';
  68718. print_r(XML_Util::splitQualifiedName('foo', 'bar'));
  68719. print '</pre>';
  68720. print "\n<br>\n";
  68721. /**
  68722. * verifying XML names
  68723. */
  68724. print 'verifying \'My private tag\':<br>';
  68725. print '<pre>';
  68726. print_r(XML_Util::isValidname('My Private Tag'));
  68727. print '</pre>';
  68728. print "\n<br><br>\n";
  68729. print 'verifying \'-MyTag\':<br>';
  68730. print '<pre>';
  68731. print_r(XML_Util::isValidname('-MyTag'));
  68732. print '</pre>';
  68733. print "\n<br><br>\n";
  68734. /**
  68735. * creating an XML tag
  68736. */
  68737. $tag = array(
  68738. 'namespace' => 'foo',
  68739. 'localPart' => 'bar',
  68740. 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'),
  68741. 'content' => 'I\'m inside the tag'
  68742. );
  68743. print 'creating a tag with namespace and local part:<br>';
  68744. print htmlentities(XML_Util::createTagFromArray($tag));
  68745. print "\n<br><br>\n";
  68746. /**
  68747. * creating an XML tag
  68748. */
  68749. $tag = array(
  68750. 'qname' => 'foo:bar',
  68751. 'namespaceUri' => 'http://foo.com',
  68752. 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'),
  68753. 'content' => 'I\'m inside the tag'
  68754. );
  68755. print 'creating a tag with qualified name and namespaceUri:<br>';
  68756. print htmlentities(XML_Util::createTagFromArray($tag));
  68757. print "\n<br><br>\n";
  68758. /**
  68759. * creating an XML tag
  68760. */
  68761. $tag = array(
  68762. 'qname' => 'bar',
  68763. 'namespaceUri' => 'http://foo.com',
  68764. 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable')
  68765. );
  68766. print 'creating an empty tag without namespace but namespace Uri:<br>';
  68767. print htmlentities(XML_Util::createTagFromArray($tag));
  68768. print "\n<br><br>\n";
  68769. /**
  68770. * creating an XML tag with more namespaces
  68771. */
  68772. $tag = array(
  68773. 'namespace' => 'foo',
  68774. 'localPart' => 'bar',
  68775. 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'),
  68776. 'content' => 'I\'m inside the tag',
  68777. 'namespaces' => array(
  68778. 'bar' => 'http://bar.com',
  68779. 'pear' => 'http://pear.php.net',
  68780. )
  68781. );
  68782. print 'creating an XML tag with more namespaces:<br />';
  68783. print htmlentities(XML_Util::createTagFromArray($tag));
  68784. print "\n<br><br>\n";
  68785. /**
  68786. * creating an XML tag with a CData Section
  68787. */
  68788. $tag = array(
  68789. 'qname' => 'foo',
  68790. 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'),
  68791. 'content' => 'I\'m inside the tag'
  68792. );
  68793. print 'creating a tag with CData section:<br>';
  68794. print htmlentities(XML_Util::createTagFromArray($tag, XML_UTIL_CDATA_SECTION));
  68795. print "\n<br><br>\n";
  68796. /**
  68797. * creating an XML tag with a CData Section
  68798. */
  68799. $tag = array(
  68800. 'qname' => 'foo',
  68801. 'attributes' => array('key' => 'value', 'argh' => 'tütü'),
  68802. 'content' =>
  68803. 'Also XHTML-tags can be created '
  68804. . 'and HTML entities can be replaced Ä ä Ü ö <>.'
  68805. );
  68806. print 'creating a tag with HTML entities:<br>';
  68807. print htmlentities(XML_Util::createTagFromArray($tag, XML_UTIL_ENTITIES_HTML));
  68808. print "\n<br><br>\n";
  68809. /**
  68810. * creating an XML tag with createTag
  68811. */
  68812. print 'creating a tag with createTag:<br>';
  68813. print htmlentities(XML_Util::createTag('myNs:myTag',
  68814. array('foo' => 'bar'),
  68815. 'This is inside the tag',
  68816. 'http://www.w3c.org/myNs#'));
  68817. print "\n<br><br>\n";
  68818. /**
  68819. * trying to create an XML tag with an array as content
  68820. */
  68821. $tag = array(
  68822. 'qname' => 'bar',
  68823. 'content' => array('foo' => 'bar')
  68824. );
  68825. print 'trying to create an XML tag with an array as content:<br>';
  68826. print '<pre>';
  68827. print_r(XML_Util::createTagFromArray($tag));
  68828. print '</pre>';
  68829. print "\n<br><br>\n";
  68830. /**
  68831. * trying to create an XML tag without a name
  68832. */
  68833. $tag = array(
  68834. 'attributes' => array('foo' => 'bar'),
  68835. );
  68836. print 'trying to create an XML tag without a name:<br>';
  68837. print '<pre>';
  68838. print_r(XML_Util::createTagFromArray($tag));
  68839. print '</pre>';
  68840. print "\n<br><br>\n";
  68841. ?>
  68842. ��������������������������������������������������������XML_Util-1.4.5/examples/example2.php����������������������������������������������������������������0000644�0001750�0001750�00000011353�13647063022�017100 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  68843. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  68844. /**
  68845. * Examples (file #2)
  68846. *
  68847. * several examples for the methods of XML_Util
  68848. *
  68849. * PHP versions 4 and 5
  68850. *
  68851. * LICENSE:
  68852. *
  68853. * Copyright (c) 2003-2008 Stephan Schmidt <schst@php.net>
  68854. * All rights reserved.
  68855. *
  68856. * Redistribution and use in source and binary forms, with or without
  68857. * modification, are permitted provided that the following conditions
  68858. * are met:
  68859. *
  68860. * * Redistributions of source code must retain the above copyright
  68861. * notice, this list of conditions and the following disclaimer.
  68862. * * Redistributions in binary form must reproduce the above copyright
  68863. * notice, this list of conditions and the following disclaimer in the
  68864. * documentation and/or other materials provided with the distribution.
  68865. * * The name of the author may not be used to endorse or promote products
  68866. * derived from this software without specific prior written permission.
  68867. *
  68868. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  68869. * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  68870. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  68871. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  68872. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  68873. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  68874. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  68875. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  68876. * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  68877. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  68878. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  68879. *
  68880. * @category XML
  68881. * @package XML_Util
  68882. * @subpackage Examples
  68883. * @author Stephan Schmidt <schst@php.net>
  68884. * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
  68885. * @license http://opensource.org/licenses/bsd-license New BSD License
  68886. * @version CVS: $Id$
  68887. * @link http://pear.php.net/package/XML_Util
  68888. */
  68889. /**
  68890. * set error level
  68891. */
  68892. error_reporting(E_ALL);
  68893. require_once 'XML/Util.php';
  68894. /**
  68895. * creating a start element
  68896. */
  68897. print 'creating a start element:<br>';
  68898. print htmlentities(XML_Util::createStartElement('myNs:myTag',
  68899. array('foo' => 'bar'), 'http://www.w3c.org/myNs#'));
  68900. print "\n<br><br>\n";
  68901. /**
  68902. * creating a start element
  68903. */
  68904. print 'creating a start element:<br>';
  68905. print htmlentities(XML_Util::createStartElement('myTag',
  68906. array(), 'http://www.w3c.org/myNs#'));
  68907. print "\n<br><br>\n";
  68908. /**
  68909. * creating a start element
  68910. */
  68911. print 'creating a start element:<br>';
  68912. print '<pre>';
  68913. print htmlentities(XML_Util::createStartElement('myTag',
  68914. array('foo' => 'bar', 'argh' => 'tomato'),
  68915. 'http://www.w3c.org/myNs#', true));
  68916. print '</pre>';
  68917. print "\n<br><br>\n";
  68918. /**
  68919. * creating an end element
  68920. */
  68921. print 'creating an end element:<br>';
  68922. print htmlentities(XML_Util::createEndElement('myNs:myTag'));
  68923. print "\n<br><br>\n";
  68924. /**
  68925. * creating a CData section
  68926. */
  68927. print 'creating a CData section:<br>';
  68928. print htmlentities(XML_Util::createCDataSection('I am content.'));
  68929. print "\n<br><br>\n";
  68930. /**
  68931. * creating a comment
  68932. */
  68933. print 'creating a comment:<br>';
  68934. print htmlentities(XML_Util::createComment('I am a comment.'));
  68935. print "\n<br><br>\n";
  68936. /**
  68937. * creating an XML tag with multiline mode
  68938. */
  68939. $tag = array(
  68940. 'qname' => 'foo:bar',
  68941. 'namespaceUri' => 'http://foo.com',
  68942. 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'),
  68943. 'content' => 'I\'m inside the tag & contain dangerous chars'
  68944. );
  68945. print 'creating a tag with qualified name and namespaceUri:<br>';
  68946. print '<pre>';
  68947. print htmlentities(XML_Util::createTagFromArray($tag,
  68948. XML_UTIL_REPLACE_ENTITIES, true));
  68949. print '</pre>';
  68950. print "\n<br><br>\n";
  68951. /**
  68952. * create an attribute string without replacing the entities
  68953. */
  68954. $atts = array('series' => 'Starsky &amp; Hutch', 'channel' => 'ABC');
  68955. print 'creating a attribute string, '
  68956. . 'entities in values already had been replaced:<br>';
  68957. print htmlentities(XML_Util::attributesToString($atts,
  68958. true, false, false, false, XML_UTIL_ENTITIES_NONE));
  68959. print "\n<br><br>\n";
  68960. /**
  68961. * using the array-syntax for attributesToString()
  68962. */
  68963. $atts = array('series' => 'Starsky &amp; Hutch', 'channel' => 'ABC');
  68964. print 'using the array-syntax for attributesToString()<br>';
  68965. print htmlentities(XML_Util::attributesToString($atts,
  68966. array('entities' => XML_UTIL_ENTITIES_NONE)));
  68967. print "\n<br><br>\n";
  68968. ?>
  68969. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/AbstractUnitTests.php����������������������������������������������������������0000644�0001750�0001750�00000000650�13647063022�020333 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  68970. /*
  68971. * Allow for PHPUnit 4.* while XML_Util is still usable on PHP 5.4
  68972. */
  68973. if (!class_exists('PHPUnit_Framework_TestCase')) {
  68974. class PHPUnit_Framework_TestCase extends \PHPUnit\Framework\TestCase {}
  68975. }
  68976. abstract class AbstractUnitTests extends \PHPUnit_Framework_TestCase
  68977. {
  68978. public function setUp()
  68979. {
  68980. // ensure the class is defined, and thus its constants are declared
  68981. new XML_Util();
  68982. }
  68983. }
  68984. ����������������������������������������������������������������������������������������XML_Util-1.4.5/tests/ApiVersionTests.php������������������������������������������������������������0000644�0001750�0001750�00000000335�13647063022�020007 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  68985. class ApiVersionTests extends AbstractUnitTests
  68986. {
  68987. /**
  68988. * @covers XML_Util::apiVersion()
  68989. */
  68990. public function testApiVersion()
  68991. {
  68992. $this->assertEquals('1.4', XML_Util::apiVersion());
  68993. }
  68994. }���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/AttributesToStringTests.php����������������������������������������������������0000644�0001750�0001750�00000017026�13647063022�021555 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  68995. class AttributesToStringTests extends AbstractUnitTests
  68996. {
  68997. /**
  68998. * @covers XML_Util::attributesToString()
  68999. */
  69000. public function testAttributesToStringBasicUsage()
  69001. {
  69002. $original = array('foo' => 'bar','boo' => 'baz',);
  69003. $expected = " boo=\"baz\" foo=\"bar\"";
  69004. $this->assertEquals($expected, XML_Util::attributesToString($original));
  69005. }
  69006. /**
  69007. * @covers XML_Util::attributesToString()
  69008. */
  69009. public function testAttributesToStringWithExplicitSortTrue()
  69010. {
  69011. $original = array('foo' => 'bar','boo' => 'baz',);
  69012. $expected = " boo=\"baz\" foo=\"bar\"";
  69013. $sort = true;
  69014. $this->assertEquals($expected, XML_Util::attributesToString($original, $sort));
  69015. }
  69016. /**
  69017. * @covers XML_Util::attributesToString()
  69018. */
  69019. public function testAttributesToStringWithExplicitSortFalse()
  69020. {
  69021. $original = array('foo' => 'bar','boo' => 'baz',);
  69022. $expected = " foo=\"bar\" boo=\"baz\"";
  69023. $sort = false;
  69024. $this->assertEquals($expected, XML_Util::attributesToString($original, $sort));
  69025. }
  69026. /**
  69027. * @covers XML_Util::attributesToString()
  69028. */
  69029. public function testAttributesToStringWithMultilineFalse()
  69030. {
  69031. $original = array('foo' => 'bar','boo' => 'baz',);
  69032. $expected = " boo=\"baz\" foo=\"bar\"";
  69033. $sort = true;
  69034. $multiline = false;
  69035. $this->assertEquals($expected, XML_Util::attributesToString($original, $sort, $multiline));
  69036. }
  69037. /**
  69038. * @covers XML_Util::attributesToString()
  69039. */
  69040. public function testAttributesToStringWithMultilineTrue()
  69041. {
  69042. $original = array('foo' => 'bar','boo' => 'baz',);
  69043. $expected =
  69044. <<< EOF
  69045. boo="baz"
  69046. foo="bar"
  69047. EOF;
  69048. $sort = true;
  69049. $multiline = true;
  69050. $this->assertEquals($expected, XML_Util::attributesToString($original, $sort, $multiline));
  69051. }
  69052. /**
  69053. * @covers XML_Util::attributesToString()
  69054. */
  69055. public function testAttributesToStringWithExplicitIndent()
  69056. {
  69057. $original = array('foo' => 'bar','boo' => 'baz',);
  69058. $expected = " boo=\"baz\"\n foo=\"bar\"";
  69059. $sort = true;
  69060. $multiline = true;
  69061. $indent = ' '; // 8 spaces
  69062. $this->assertEquals($expected, XML_Util::attributesToString($original, $sort, $multiline, $indent));
  69063. }
  69064. /**
  69065. * @covers XML_Util::attributesToString()
  69066. */
  69067. public function testAttributesToStringWithExplicitLinebreak()
  69068. {
  69069. $original = array('foo' => 'bar','boo' => 'baz',);
  69070. $expected = " boo=\"baz\"\n^foo=\"bar\"";
  69071. $sort = true;
  69072. $multiline = true;
  69073. $linebreak = '^'; // some dummy character
  69074. $this->assertEquals($expected, XML_Util::attributesToString($original, $sort, $multiline, $linebreak));
  69075. }
  69076. /**
  69077. * @covers XML_Util::attributesToString()
  69078. */
  69079. public function testAttributesToStringWithOptionsThatIncludesSort()
  69080. {
  69081. $original = array('foo' => 'bar','boo' => 'baz',);
  69082. $options = array(
  69083. 'multiline' => true,
  69084. 'indent' => '----',
  69085. 'linebreak' => "^",
  69086. 'entities' => XML_UTIL_ENTITIES_XML,
  69087. 'sort' => true,
  69088. );
  69089. $expected = " boo=\"baz\"\n----foo=\"bar\"";
  69090. $this->assertEquals($expected, XML_Util::attributesToString($original, $options));
  69091. }
  69092. /**
  69093. * @covers XML_Util::attributesToString()
  69094. */
  69095. public function testAttributesToStringWithOptionsThatExcludesSort()
  69096. {
  69097. $original = array('foo' => 'bar','boo' => 'baz',);
  69098. $options = array(
  69099. 'multiline' => true,
  69100. 'indent' => '----',
  69101. 'linebreak' => "^",
  69102. 'entities' => XML_UTIL_ENTITIES_XML,
  69103. );
  69104. $expected = " boo=\"baz\"\n----foo=\"bar\"";
  69105. $this->assertEquals($expected, XML_Util::attributesToString($original, $options));
  69106. }
  69107. /**
  69108. * @covers XML_Util::attributesToString()
  69109. */
  69110. public function testAttributesToStringWithEntitiesNone()
  69111. {
  69112. $original = array("foo" => "b@&r", "boo" => "b><z");
  69113. $expected = " boo=\"b><z\" foo=\"b@&r\"";
  69114. $sort = true;
  69115. $multiline = false;
  69116. $linebreak = ' ';
  69117. $this->assertEquals($expected, XML_Util::attributesToString($original, $sort, $multiline, $linebreak, PHP_EOL, XML_UTIL_ENTITIES_NONE));
  69118. }
  69119. /**
  69120. * @covers XML_Util::attributesToString()
  69121. */
  69122. public function testAttributesToStringWithEntitiesXml()
  69123. {
  69124. $original = array("foo" => "b@&r", "boo" => "b><z");
  69125. $expected = " boo=\"b&gt;&lt;z\" foo=\"b@&amp;r\"";
  69126. $sort = true;
  69127. $multiline = false;
  69128. $linebreak = ' ';
  69129. $this->assertEquals($expected, XML_Util::attributesToString($original, $sort, $multiline, $linebreak, PHP_EOL, XML_UTIL_ENTITIES_XML));
  69130. }
  69131. /**
  69132. * @covers XML_Util::attributesToString()
  69133. */
  69134. public function testAttributesToStringWithEntitiesXmlRequired()
  69135. {
  69136. $original = array("foo" => "b@&r", "boo" => "b><z");
  69137. $expected = " boo=\"b>&lt;z\" foo=\"b@&amp;r\"";
  69138. $sort = true;
  69139. $multiline = false;
  69140. $linebreak = ' ';
  69141. $this->assertEquals($expected, XML_Util::attributesToString($original, $sort, $multiline, $linebreak, PHP_EOL, XML_UTIL_ENTITIES_XML_REQUIRED));
  69142. }
  69143. /**
  69144. * @covers XML_Util::attributesToString()
  69145. */
  69146. public function testAttributesToStringWithEntitiesHtml()
  69147. {
  69148. $original = array("foo" => "b@&r", "boo" => "b><z");
  69149. $expected = " boo=\"b&gt;&lt;z\" foo=\"b@&amp;r\"";
  69150. $sort = true;
  69151. $multiline = false;
  69152. $linebreak = ' ';
  69153. $this->assertEquals($expected, XML_Util::attributesToString($original, $sort, $multiline, $linebreak, PHP_EOL, XML_UTIL_ENTITIES_HTML));
  69154. }
  69155. /**
  69156. * Tag attributes should not be treated as CDATA,
  69157. * so the operation will instead quietly use XML_UTIL_ENTITIES_XML.
  69158. *
  69159. * @covers XML_Util::attributesToString()
  69160. */
  69161. public function testAttributesToStringWithCDataSectionForSingleAttribute()
  69162. {
  69163. $original = array('foo' => 'bar'); // need exactly one attribute here
  69164. $options = array(
  69165. 'sort' => true, // doesn't matter for this testcase
  69166. 'multiline' => false, // doesn't matter for this testcase
  69167. 'indent' => null, // doesn't matter for this testcase
  69168. 'linebreak' => null, // doesn't matter for this testcase
  69169. 'entities' => XML_UTIL_CDATA_SECTION, // DOES matter for this testcase
  69170. );
  69171. $expected = " foo=\"bar\"";
  69172. $this->assertEquals($expected, XML_Util::attributesToString($original, $options));
  69173. }
  69174. /**
  69175. * Tag attributes should not be treated as CDATA,
  69176. * so the operation will instead quietly use XML_UTIL_ENTITIES_XML.
  69177. *
  69178. * @covers XML_Util::attributesToString()
  69179. */
  69180. public function testAttributesToStringWithCDataSectionForMultipleAttributesAndMultilineFalse()
  69181. {
  69182. $original = array('foo' => 'bar', 'boo' => 'baz'); // need more than one attribute here
  69183. $options = array(
  69184. 'sort' => true, // doesn't matter for this testcase
  69185. 'multiline' => false, // DOES matter for this testcase, must be false
  69186. 'indent' => null, // doesn't matter for this testcase
  69187. 'linebreak' => null, // doesn't matter for this testcase
  69188. 'entities' => XML_UTIL_CDATA_SECTION, // DOES matter for this testcase
  69189. );
  69190. $expected = " boo=\"baz\" foo=\"bar\"";
  69191. $this->assertEquals($expected, XML_Util::attributesToString($original, $options));
  69192. }
  69193. }
  69194. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/CollapseEmptyTagsTests.php�����������������������������������������������������0000644�0001750�0001750�00000010401�13647063022�021323 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  69195. class CollapseEmptyTagsTests extends AbstractUnitTests
  69196. {
  69197. /**
  69198. * @covers XML_Util::collapseEmptyTags()
  69199. */
  69200. public function testCollapseEmptyTagsBasicUsage()
  69201. {
  69202. $emptyTag = "<foo></foo>";
  69203. $expected = "<foo />";
  69204. $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag));
  69205. }
  69206. /**
  69207. * @covers XML_Util::collapseEmptyTags()
  69208. */
  69209. public function testCollapseEmptyTagsBasicUsageAlongsideNonemptyTag()
  69210. {
  69211. $emptyTag = "<foo></foo>";
  69212. $otherTag = "<bar>baz</bar>";
  69213. $expected = "<foo /><bar>baz</bar>";
  69214. $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag . $otherTag));
  69215. }
  69216. /**
  69217. * @covers XML_Util::collapseEmptyTags()
  69218. */
  69219. public function testCollapseEmptyTagsOnOneEmptyTagWithCollapseAll()
  69220. {
  69221. $emptyTag = "<foo></foo>";
  69222. $expected = "<foo />";
  69223. $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag, XML_UTIL_COLLAPSE_ALL));
  69224. }
  69225. /**
  69226. * @covers XML_Util::collapseEmptyTags()
  69227. */
  69228. public function testCollapseEmptyTagsOnOneEmptyTagAlongsideNonemptyTagWithCollapseAll()
  69229. {
  69230. $emptyTag = "<foo></foo>";
  69231. $otherTag = "<bar>baz</bar>";
  69232. $expected = "<foo /><bar>baz</bar>";
  69233. $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag . $otherTag, XML_UTIL_COLLAPSE_ALL));
  69234. }
  69235. /**
  69236. * @covers XML_Util::collapseEmptyTags()
  69237. */
  69238. public function testCollapseEmptyTagsOnOneEmptyTagAlongsideNonemptyTagAlongsideEmptyTagWithCollapseAll()
  69239. {
  69240. $emptyTag = "<foo></foo>";
  69241. $otherTag = "<bar>baz</bar>";
  69242. $expected = "<foo /><bar>baz</bar><foo />";
  69243. $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag . $otherTag . $emptyTag, XML_UTIL_COLLAPSE_ALL));
  69244. }
  69245. /**
  69246. * @covers XML_Util::collapseEmptyTags()
  69247. */
  69248. public function testCollapseEmptyTagsOnOneEmptyPrefixedTagAlongsideNonemptyTagAlongsideEmptyPrefixedTagWithCollapseAll()
  69249. {
  69250. $emptyTag = "<foo:foo2></foo:foo2>";
  69251. $otherTag = "<bar>baz</bar>";
  69252. $expected = "<foo:foo2 /><bar>baz</bar><foo:foo2 />";
  69253. $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag . $otherTag . $emptyTag, XML_UTIL_COLLAPSE_ALL));
  69254. }
  69255. /**
  69256. * @covers XML_Util::collapseEmptyTags()
  69257. */
  69258. public function testCollapseEmptyTagsOnOneEmptyNsPrefixedTagAlongsideNonemptyTagAlongsideEmptyNsPrefixedTagWithCollapseAll()
  69259. {
  69260. $emptyTag = "<http://foo.com:foo2></http://foo.com:foo2>";
  69261. $otherTag = "<bar>baz</bar>";
  69262. $expected = "<http://foo.com:foo2 /><bar>baz</bar><http://foo.com:foo2 />";
  69263. $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag . $otherTag . $emptyTag, XML_UTIL_COLLAPSE_ALL));
  69264. }
  69265. /**
  69266. * @covers XML_Util::collapseEmptyTags()
  69267. */
  69268. public function testCollapseEmptyTagsOnOneEmptyTagWithCollapseXhtml()
  69269. {
  69270. $emptyTag = "<foo></foo>";
  69271. $expected = "<foo></foo>";
  69272. $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag, XML_UTIL_COLLAPSE_XHTML_ONLY));
  69273. }
  69274. /**
  69275. * @covers XML_Util::collapseEmptyTags()
  69276. */
  69277. public function testCollapseEmptyTagsOnOneEmptyTagAlongsideNonemptyTagWithCollapseXhtml()
  69278. {
  69279. $emptyTag = "<foo></foo>";
  69280. $otherTag = "<bar>baz</bar>";
  69281. $xhtmlTag = "<br></br>";
  69282. $expected = "<foo></foo><br /><bar>baz</bar>";
  69283. $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag . $xhtmlTag . $otherTag, XML_UTIL_COLLAPSE_XHTML_ONLY));
  69284. }
  69285. /**
  69286. * @covers XML_Util::collapseEmptyTags()
  69287. */
  69288. public function testCollapseEmptyTagsOnOneEmptyTagWithCollapseNone()
  69289. {
  69290. $emptyTag = "<foo></foo>";
  69291. $expected = "<foo></foo>";
  69292. $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag, XML_UTIL_COLLAPSE_NONE));
  69293. }
  69294. /**
  69295. * @covers XML_Util::collapseEmptyTags()
  69296. */
  69297. public function testCollapseEmptyTagsOnOneEmptyTagAlongsideNonemptyTagWithCollapseNone()
  69298. {
  69299. $emptyTag = "<foo></foo>";
  69300. $otherTag = "<bar>baz</bar>";
  69301. $expected = "<foo></foo><bar>baz</bar>";
  69302. $this->assertEquals($expected, XML_Util::collapseEmptyTags($emptyTag . $otherTag, XML_UTIL_COLLAPSE_NONE));
  69303. }
  69304. }
  69305. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/CreateCDataSectionTests.php����������������������������������������������������0000644�0001750�0001750�00000000552�13647063022�021356 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  69306. class CreateCDataSectionTests extends AbstractUnitTests
  69307. {
  69308. /**
  69309. * @covers XML_Util::createCDataSection()
  69310. */
  69311. public function testCreateCDataSectionBasicUsage()
  69312. {
  69313. $original = "I am content.";
  69314. $expected ="<![CDATA[I am content.]]>";
  69315. $this->assertEquals($expected, XML_Util::createCDataSection($original));
  69316. }
  69317. }
  69318. ������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/CreateCommentTests.php���������������������������������������������������������0000644�0001750�0001750�00000000524�13647063022�020456 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  69319. class CreateCommentTests extends AbstractUnitTests
  69320. {
  69321. /**
  69322. * @covers XML_Util::createComment()
  69323. */
  69324. public function testCreateCommentBasicUsage()
  69325. {
  69326. $original = "I am comment.";
  69327. $expected = "<!-- I am comment. -->";
  69328. $this->assertEquals($expected, XML_Util::createComment($original));
  69329. }
  69330. }
  69331. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/CreateEndElementTests.php������������������������������������������������������0000644�0001750�0001750�00000001145�13647063022�021074 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  69332. class CreateEndElementTests extends AbstractUnitTests
  69333. {
  69334. /**
  69335. * @covers XML_Util::createEndElement()
  69336. */
  69337. public function testCreateEndElementBasicUsage()
  69338. {
  69339. $original = "myTag";
  69340. $expected = "</myTag>";
  69341. $this->assertEquals($expected, XML_Util::createEndElement($original));
  69342. }
  69343. /**
  69344. * @covers XML_Util::createEndElement()
  69345. */
  69346. public function testCreateEndElementWithNamespacedTag()
  69347. {
  69348. $original = "myNs:myTag";
  69349. $expected = "</myNs:myTag>";
  69350. $this->assertEquals($expected, XML_Util::createEndElement($original));
  69351. }
  69352. }
  69353. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/CreateStartElementTests.php����������������������������������������������������0000644�0001750�0001750�00000012442�13647063022�021465 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  69354. class CreateStartElementTests extends AbstractUnitTests
  69355. {
  69356. /**
  69357. * @covers XML_Util::createStartElement()
  69358. */
  69359. public function testCreateStartElementForTagOnly()
  69360. {
  69361. $original = "myNs:myTag";
  69362. $expected = "<myNs:myTag>";
  69363. $this->assertEquals($expected, XML_Util::createStartElement($original));
  69364. }
  69365. /**
  69366. * @covers XML_Util::createStartElement()
  69367. */
  69368. public function testCreateStartElementForTagWithAttributes()
  69369. {
  69370. $originalTag = "myNs:myTag";
  69371. $originalAttributes = array("foo" => "bar");
  69372. $expected = "<myNs:myTag foo=\"bar\">";
  69373. $this->assertEquals($expected, XML_Util::createStartElement($originalTag, $originalAttributes));
  69374. }
  69375. /**
  69376. * @covers XML_Util::createStartElement()
  69377. */
  69378. public function testCreateStartElementForTagWithEmptyAttributes()
  69379. {
  69380. $originalTag = "myNs:myTag";
  69381. $originalAttributes = "";
  69382. $expected = "<myNs:myTag>";
  69383. $this->assertEquals($expected, XML_Util::createStartElement($originalTag, $originalAttributes));
  69384. }
  69385. /**
  69386. * @covers XML_Util::createStartElement()
  69387. */
  69388. public function testCreateStartElementForTagWithAttributesAndNamespace()
  69389. {
  69390. $originalTag = "myNs:myTag";
  69391. $originalAttributes = array("foo" => "bar");
  69392. $originalNamespace = "http://www.w3c.org/myNs#";
  69393. $expected = "<myNs:myTag foo=\"bar\" xmlns:myNs=\"http://www.w3c.org/myNs#\">";
  69394. $this->assertEquals($expected, XML_Util::createStartElement($originalTag, $originalAttributes, $originalNamespace));
  69395. }
  69396. /**
  69397. * @covers XML_Util::createStartElement()
  69398. */
  69399. public function testCreateStartElementForTagWithEmptyAttributesAndNonUriNamespace()
  69400. {
  69401. $originalTag = "myTag";
  69402. $originalAttributes = "";
  69403. $originalNamespace = "foo";
  69404. $expected = "<myTag xmlns=\"foo\">";
  69405. $this->assertEquals($expected, XML_Util::createStartElement($originalTag, $originalAttributes, $originalNamespace));
  69406. }
  69407. /**
  69408. * @covers XML_Util::createStartElement()
  69409. */
  69410. public function testCreateStartElementForTagWithAttributesAndNamespaceWithMultiline()
  69411. {
  69412. $originalTag = "myNs:myTag";
  69413. $originalAttributes = array("foo" => "bar");
  69414. $originalNamespace = "http://www.w3c.org/myNs#";
  69415. $expected =
  69416. <<< EOF
  69417. <myNs:myTag foo="bar"
  69418. xmlns:myNs="http://www.w3c.org/myNs#">
  69419. EOF;
  69420. $multiline = true;
  69421. $this->assertEquals($expected, XML_Util::createStartElement($originalTag, $originalAttributes, $originalNamespace, $multiline));
  69422. }
  69423. /**
  69424. * @covers XML_Util::createStartElement()
  69425. */
  69426. public function testCreateStartElementForTagWithAttributesAndNamespaceWithMultilineAndIndent()
  69427. {
  69428. $originalTag = "myNs:myTag";
  69429. $originalAttributes = array("foo" => "bar");
  69430. $originalNamespace = "http://www.w3c.org/myNs#";
  69431. $expected =
  69432. <<< EOF
  69433. <myNs:myTag foo="bar"
  69434. xmlns:myNs="http://www.w3c.org/myNs#">
  69435. EOF;
  69436. $multiline = true;
  69437. $indent = " ";
  69438. $this->assertEquals($expected, XML_Util::createStartElement($originalTag, $originalAttributes, $originalNamespace, $multiline, $indent));
  69439. }
  69440. /**
  69441. * @covers XML_Util::createStartElement()
  69442. */
  69443. public function testCreateStartElementForTagWithAttributesAndNamespaceWithMultilineAndIndentAndLinebreak()
  69444. {
  69445. $originalTag = "myNs:myTag";
  69446. $originalAttributes = array("foo" => "bar");
  69447. $originalNamespace = "http://www.w3c.org/myNs#";
  69448. $expected = "<myNs:myTag foo=\"bar\"^ xmlns:myNs=\"http://www.w3c.org/myNs#\">";
  69449. $multiline = true;
  69450. $indent = " ";
  69451. $linebreak = "^";
  69452. $this->assertEquals($expected, XML_Util::createStartElement($originalTag, $originalAttributes, $originalNamespace, $multiline, $indent, $linebreak));
  69453. }
  69454. /**
  69455. * @covers XML_Util::createStartElement()
  69456. */
  69457. public function testCreateStartElementForTagWithAttributesAndNamespaceWithMultilineAndIndentAndLinebreakAndSortAttributesIsTrue()
  69458. {
  69459. $originalTag = "myNs:myTag";
  69460. $originalAttributes = array("foo" => "bar", "boo" => "baz");
  69461. $originalNamespace = "http://www.w3c.org/myNs#";
  69462. $expected = "<myNs:myTag boo=\"baz\"^ foo=\"bar\"^ xmlns:myNs=\"http://www.w3c.org/myNs#\">";
  69463. $multiline = true;
  69464. $indent = " ";
  69465. $linebreak = "^";
  69466. $sortAttributes = true;
  69467. $this->assertEquals($expected, XML_Util::createStartElement($originalTag, $originalAttributes, $originalNamespace, $multiline, $indent, $linebreak, $sortAttributes));
  69468. }
  69469. /**
  69470. * @covers XML_Util::createStartElement()
  69471. */
  69472. public function testCreateStartElementForTagWithAttributesAndNamespaceWithMultilineAndIndentAndLinebreakAndSortAttributesIsFalse()
  69473. {
  69474. $originalTag = "myNs:myTag";
  69475. $originalAttributes = array("foo" => "bar", "boo" => "baz");
  69476. $originalNamespace = "http://www.w3c.org/myNs#";
  69477. $expected = "<myNs:myTag foo=\"bar\"^ boo=\"baz\"^ xmlns:myNs=\"http://www.w3c.org/myNs#\">";
  69478. $multiline = true;
  69479. $indent = " ";
  69480. $linebreak = "^";
  69481. $sortAttributes = false;
  69482. $this->assertEquals($expected, XML_Util::createStartElement($originalTag, $originalAttributes, $originalNamespace, $multiline, $indent, $linebreak, $sortAttributes));
  69483. }
  69484. }
  69485. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/CreateTagTests.php�������������������������������������������������������������0000644�0001750�0001750�00000017452�13647063022�017577 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  69486. class CreateTagTests extends AbstractUnitTests
  69487. {
  69488. /**
  69489. * @covers XML_Util::createTag()
  69490. */
  69491. public function testCreateTagForTagWithAttributes()
  69492. {
  69493. $originalTag = "myNs:myTag";
  69494. $originalAttributes = array("foo" => "bar");
  69495. $expected = "<myNs:myTag foo=\"bar\" />";
  69496. $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes));
  69497. }
  69498. /**
  69499. * @covers XML_Util::createTag()
  69500. */
  69501. public function testCreateTagForTagWithAttributesAndContent()
  69502. {
  69503. $originalTag = "myNs:myTag";
  69504. $originalAttributes = array("foo" => "bar");
  69505. $originalContent = "This is inside the tag";
  69506. $expected = "<myNs:myTag foo=\"bar\">This is inside the tag</myNs:myTag>";
  69507. $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent));
  69508. }
  69509. /**
  69510. * @covers XML_Util::createTag()
  69511. */
  69512. public function testCreateTagForTagWithAttributesAndContentAndNamespace()
  69513. {
  69514. $originalTag = "myNs:myTag";
  69515. $originalAttributes = array("foo" => "bar");
  69516. $originalContent = "This is inside the tag";
  69517. $originalNamespace = "http://www.w3c.org/myNs#";
  69518. $expected = "<myNs:myTag foo=\"bar\" xmlns:myNs=\"http://www.w3c.org/myNs#\">This is inside the tag</myNs:myTag>";
  69519. $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent, $originalNamespace));
  69520. }
  69521. /**
  69522. * @covers XML_Util::createTag()
  69523. */
  69524. public function testCreateTagForTagWithAttributesAndContentAndNamespaceWithCDataSection()
  69525. {
  69526. $originalTag = "myNs:myTag";
  69527. $originalAttributes = array("foo" => "bar");
  69528. $originalContent = "This is inside the tag and has < & @ > in it";
  69529. $originalNamespace = "http://www.w3c.org/myNs#";
  69530. $expected = "<myNs:myTag foo=\"bar\" xmlns:myNs=\"http://www.w3c.org/myNs#\"><![CDATA[This is inside the tag and has < & @ > in it]]></myNs:myTag>";
  69531. $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent, $originalNamespace, XML_UTIL_CDATA_SECTION));
  69532. }
  69533. /**
  69534. * @covers XML_Util::createTag()
  69535. */
  69536. public function testCreateTagForTagWithAttributesAndContentAndNamespaceWithReplaceEntities()
  69537. {
  69538. $originalTag = "myNs:myTag";
  69539. $originalAttributes = array("foo" => "bar");
  69540. $originalContent = "This is inside the tag and has < & @ > in it";
  69541. $originalNamespace = "http://www.w3c.org/myNs#";
  69542. $expected = "<myNs:myTag foo=\"bar\" xmlns:myNs=\"http://www.w3c.org/myNs#\">This is inside the tag and has &lt; &amp; @ &gt; in it</myNs:myTag>";
  69543. $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent, $originalNamespace, XML_UTIL_REPLACE_ENTITIES));
  69544. }
  69545. /**
  69546. * @covers XML_Util::createTag()
  69547. */
  69548. public function testCreateTagForTagWithAttributesAndContentAndNamespaceWithReplaceEntitiesAndMultilineFalse()
  69549. {
  69550. $originalTag = "myNs:myTag";
  69551. $originalAttributes = array("foo" => "bar");
  69552. $originalContent = "This is inside the tag and has < & @ > in it";
  69553. $originalNamespace = "http://www.w3c.org/myNs#";
  69554. $multiline = false;
  69555. $expected = "<myNs:myTag foo=\"bar\" xmlns:myNs=\"http://www.w3c.org/myNs#\">This is inside the tag and has &lt; &amp; @ &gt; in it</myNs:myTag>";
  69556. $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent, $originalNamespace, XML_UTIL_REPLACE_ENTITIES, $multiline));
  69557. }
  69558. /**
  69559. * @covers XML_Util::createTag()
  69560. */
  69561. public function testCreateTagForTagWithAttributesAndContentAndNamespaceWithReplaceEntitiesAndMultilineTrue()
  69562. {
  69563. $originalTag = "myNs:myTag";
  69564. $originalAttributes = array("foo" => "bar");
  69565. $originalContent = "This is inside the tag and has < & @ > in it";
  69566. $originalNamespace = "http://www.w3c.org/myNs#";
  69567. $multiline = true;
  69568. $expected =
  69569. <<< EOF
  69570. <myNs:myTag foo="bar"
  69571. xmlns:myNs="http://www.w3c.org/myNs#">This is inside the tag and has &lt; &amp; @ &gt; in it</myNs:myTag>
  69572. EOF;
  69573. $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent, $originalNamespace, XML_UTIL_REPLACE_ENTITIES, $multiline));
  69574. }
  69575. /**
  69576. * @covers XML_Util::createTag()
  69577. */
  69578. public function testCreateTagForTagWithAttributesAndContentAndNamespaceWithReplaceEntitiesAndMultilineTrueAndIndent()
  69579. {
  69580. $originalTag = "myNs:myTag";
  69581. $originalAttributes = array("foo" => "bar");
  69582. $originalContent = "This is inside the tag and has < & @ > in it";
  69583. $originalNamespace = "http://www.w3c.org/myNs#";
  69584. $multiline = true;
  69585. $indent = " ";
  69586. $expected =
  69587. <<< EOF
  69588. <myNs:myTag foo="bar"
  69589. xmlns:myNs="http://www.w3c.org/myNs#">This is inside the tag and has &lt; &amp; @ &gt; in it</myNs:myTag>
  69590. EOF;
  69591. $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent, $originalNamespace, XML_UTIL_REPLACE_ENTITIES, $multiline, $indent));
  69592. }
  69593. /**
  69594. * @covers XML_Util::createTag()
  69595. */
  69596. public function testCreateTagForTagWithAttributesAndContentAndNamespaceWithReplaceEntitiesAndMultilineTrueAndIndentAndLinebreak()
  69597. {
  69598. $originalTag = "myNs:myTag";
  69599. $originalAttributes = array("foo" => "bar");
  69600. $originalContent = "This is inside the tag and has < & @ > in it";
  69601. $originalNamespace = "http://www.w3c.org/myNs#";
  69602. $multiline = true;
  69603. $indent = " ";
  69604. $linebreak = "^";
  69605. $expected = "<myNs:myTag foo=\"bar\"^ xmlns:myNs=\"http://www.w3c.org/myNs#\">This is inside the tag and has &lt; &amp; @ &gt; in it</myNs:myTag>";
  69606. $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent, $originalNamespace, XML_UTIL_REPLACE_ENTITIES, $multiline, $indent, $linebreak));
  69607. }
  69608. /**
  69609. * @covers XML_Util::createTag()
  69610. */
  69611. public function testCreateTagForTagWithAttributesAndContentAndNamespaceWithReplaceEntitiesAndMultilineTrueAndIndentAndLinebreakAndSortAttributesTrue()
  69612. {
  69613. $originalTag = "myNs:myTag";
  69614. $originalAttributes = array("foo" => "bar", "boo" => "baz");
  69615. $originalContent = "This is inside the tag and has < & @ > in it";
  69616. $originalNamespace = "http://www.w3c.org/myNs#";
  69617. $multiline = true;
  69618. $indent = " ";
  69619. $linebreak = "^";
  69620. $sortAttributes = true;
  69621. $expected = "<myNs:myTag boo=\"baz\"^ foo=\"bar\"^ xmlns:myNs=\"http://www.w3c.org/myNs#\">This is inside the tag and has &lt; &amp; @ &gt; in it</myNs:myTag>";
  69622. $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent, $originalNamespace, XML_UTIL_REPLACE_ENTITIES, $multiline, $indent, $linebreak, $sortAttributes));
  69623. }
  69624. /**
  69625. * @covers XML_Util::createTag()
  69626. */
  69627. public function testCreateTagForTagWithAttributesAndContentAndNamespaceWithReplaceEntitiesAndMultilineTrueAndIndentAndLinebreakAndSortAttributesFalse()
  69628. {
  69629. $originalTag = "myNs:myTag";
  69630. $originalAttributes = array("foo" => "bar", "boo" => "baz");
  69631. $originalContent = "This is inside the tag and has < & @ > in it";
  69632. $originalNamespace = "http://www.w3c.org/myNs#";
  69633. $multiline = true;
  69634. $indent = " ";
  69635. $linebreak = "^";
  69636. $sortAttributes = false;
  69637. $expected = "<myNs:myTag foo=\"bar\"^ boo=\"baz\"^ xmlns:myNs=\"http://www.w3c.org/myNs#\">This is inside the tag and has &lt; &amp; @ &gt; in it</myNs:myTag>";
  69638. $this->assertEquals($expected, XML_Util::createTag($originalTag, $originalAttributes, $originalContent, $originalNamespace, XML_UTIL_REPLACE_ENTITIES, $multiline, $indent, $linebreak, $sortAttributes));
  69639. }
  69640. }
  69641. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/CreateTagFromArrayTests.php����������������������������������������������������0000644�0001750�0001750�00000032264�13647063022�021420 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  69642. class CreateTagFromArrayTests extends AbstractUnitTests
  69643. {
  69644. /**
  69645. * @covers XML_Util::createTagFromArray()
  69646. */
  69647. public function testCreateTagFromArrayWithQname()
  69648. {
  69649. $original = array(
  69650. "qname" => "foo:bar",
  69651. );
  69652. $expected = "<foo:bar />";
  69653. $this->assertEquals($expected, XML_Util::createTagFromArray($original));
  69654. }
  69655. /**
  69656. * @covers XML_Util::createTagFromArray()
  69657. */
  69658. public function testCreateTagFromArrayWithQnameAndNamespace()
  69659. {
  69660. $original = array(
  69661. "qname" => "foo:bar",
  69662. "namespaceUri" => "http://foo.com",
  69663. );
  69664. $expected = "<foo:bar xmlns:foo=\"http://foo.com\" />";
  69665. $this->assertEquals($expected, XML_Util::createTagFromArray($original));
  69666. }
  69667. /**
  69668. * @covers XML_Util::createTagFromArray()
  69669. */
  69670. public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributes()
  69671. {
  69672. $original = array(
  69673. "qname" => "foo:bar",
  69674. "namespaceUri" => "http://foo.com",
  69675. "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ),
  69676. );
  69677. $expected = "<foo:bar argh=\"fruit&amp;vegetable\" key=\"value\" xmlns:foo=\"http://foo.com\" />";
  69678. $this->assertEquals($expected, XML_Util::createTagFromArray($original));
  69679. }
  69680. /**
  69681. * @covers XML_Util::createTagFromArray()
  69682. */
  69683. public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributesAndContent()
  69684. {
  69685. $original = array(
  69686. "qname" => "foo:bar",
  69687. "namespaceUri" => "http://foo.com",
  69688. "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ),
  69689. "content" => "I'm inside the tag",
  69690. );
  69691. $expected = "<foo:bar argh=\"fruit&amp;vegetable\" key=\"value\" xmlns:foo=\"http://foo.com\">I&apos;m inside the tag</foo:bar>";
  69692. $this->assertEquals($expected, XML_Util::createTagFromArray($original));
  69693. }
  69694. /**
  69695. * @covers XML_Util::createTagFromArray()
  69696. */
  69697. public function testCreateTagFromArrayWithQnameAndAttributesAndContent()
  69698. {
  69699. $original = array(
  69700. "qname" => "foo:bar",
  69701. "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ),
  69702. "content" => "I'm inside the tag",
  69703. );
  69704. $expected = "<foo:bar argh=\"fruit&amp;vegetable\" key=\"value\">I&apos;m inside the tag</foo:bar>";
  69705. $this->assertEquals($expected, XML_Util::createTagFromArray($original));
  69706. }
  69707. /**
  69708. * @covers XML_Util::createTagFromArray()
  69709. */
  69710. public function testCreateTagFromArrayWithQnameAndNamespaceAndContent()
  69711. {
  69712. $original = array(
  69713. "qname" => "foo:bar",
  69714. "namespaceUri" => "http://foo.com",
  69715. "content" => "I'm inside the tag",
  69716. );
  69717. $expected = "<foo:bar xmlns:foo=\"http://foo.com\">I&apos;m inside the tag</foo:bar>";
  69718. $this->assertEquals($expected, XML_Util::createTagFromArray($original));
  69719. }
  69720. /**
  69721. * @covers XML_Util::createTagFromArray()
  69722. */
  69723. public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributesAndContentWithEntitiesNone()
  69724. {
  69725. $original = array(
  69726. "qname" => "foo:bar",
  69727. "namespaceUri" => "http://foo.com",
  69728. "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ),
  69729. "content" => "I'm inside the tag",
  69730. );
  69731. $expected = "<foo:bar argh=\"fruit&amp;vegetable\" key=\"value\" xmlns:foo=\"http://foo.com\">I'm inside the tag</foo:bar>";
  69732. $this->assertEquals($expected, XML_Util::createTagFromArray($original, XML_UTIL_ENTITIES_NONE));
  69733. }
  69734. /**
  69735. * @covers XML_Util::createTagFromArray()
  69736. */
  69737. public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributesAndContentWithReplaceEntities()
  69738. {
  69739. $original = array(
  69740. "qname" => "foo:bar",
  69741. "namespaceUri" => "http://foo.com",
  69742. "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ),
  69743. "content" => "I'm inside the tag",
  69744. );
  69745. $expected = "<foo:bar argh=\"fruit&amp;vegetable\" key=\"value\" xmlns:foo=\"http://foo.com\">I&apos;m inside the tag</foo:bar>";
  69746. $this->assertEquals($expected, XML_Util::createTagFromArray($original, XML_UTIL_REPLACE_ENTITIES));
  69747. }
  69748. /**
  69749. * @covers XML_Util::createTagFromArray()
  69750. */
  69751. public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributesAndContentWithReplaceEntitiesAndMultilineFalse()
  69752. {
  69753. $original = array(
  69754. "qname" => "foo:bar",
  69755. "namespaceUri" => "http://foo.com",
  69756. "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ),
  69757. "content" => "I'm inside the tag",
  69758. );
  69759. $multiline = false;
  69760. $expected = "<foo:bar argh=\"fruit&amp;vegetable\" key=\"value\" xmlns:foo=\"http://foo.com\">I&apos;m inside the tag</foo:bar>";
  69761. $this->assertEquals($expected, XML_Util::createTagFromArray($original, XML_UTIL_REPLACE_ENTITIES, $multiline));
  69762. }
  69763. /**
  69764. * @covers XML_Util::createTagFromArray()
  69765. */
  69766. public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributesAndContentWithReplaceEntitiesAndMultilineTrue()
  69767. {
  69768. $original = array(
  69769. "qname" => "foo:bar",
  69770. "namespaceUri" => "http://foo.com",
  69771. "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ),
  69772. "content" => "I'm inside the tag",
  69773. );
  69774. $multiline = true;
  69775. $expected =
  69776. <<< EOF
  69777. <foo:bar argh="fruit&amp;vegetable"
  69778. key="value"
  69779. xmlns:foo="http://foo.com">I&apos;m inside the tag</foo:bar>
  69780. EOF;
  69781. $this->assertEquals($expected, XML_Util::createTagFromArray($original, XML_UTIL_REPLACE_ENTITIES, $multiline));
  69782. }
  69783. /**
  69784. * @covers XML_Util::createTagFromArray()
  69785. */
  69786. public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributesAndContentWithReplaceEntitiesAndMultilineTrueAndIndent()
  69787. {
  69788. $original = array(
  69789. "qname" => "foo:bar",
  69790. "namespaceUri" => "http://foo.com",
  69791. "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ),
  69792. "content" => "I'm inside the tag",
  69793. );
  69794. $multiline = true;
  69795. $indent = " ";
  69796. $expected =
  69797. <<< EOF
  69798. <foo:bar argh="fruit&amp;vegetable"
  69799. key="value"
  69800. xmlns:foo="http://foo.com">I&apos;m inside the tag</foo:bar>
  69801. EOF;
  69802. $this->assertEquals($expected, XML_Util::createTagFromArray($original, XML_UTIL_REPLACE_ENTITIES, $multiline, $indent));
  69803. }
  69804. /**
  69805. * @covers XML_Util::createTagFromArray()
  69806. */
  69807. public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributesAndContentWithReplaceEntitiesAndMultilineTrueAndIndentAndLinebreak()
  69808. {
  69809. $original = array(
  69810. "qname" => "foo:bar",
  69811. "namespaceUri" => "http://foo.com",
  69812. "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ),
  69813. "content" => "I'm inside the tag",
  69814. );
  69815. $multiline = true;
  69816. $indent = " ";
  69817. $linebreak = "^";
  69818. $expected = "<foo:bar argh=\"fruit&amp;vegetable\"^ key=\"value\"^ xmlns:foo=\"http://foo.com\">I&apos;m inside the tag</foo:bar>";
  69819. $this->assertEquals($expected, XML_Util::createTagFromArray($original, XML_UTIL_REPLACE_ENTITIES, $multiline, $indent, $linebreak));
  69820. }
  69821. /**
  69822. * @covers XML_Util::createTagFromArray()
  69823. */
  69824. public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributesAndContentWithReplaceEntitiesAndMultilineTrueAndIndentAndLinebreakAndSortAttributesTrue()
  69825. {
  69826. $original = array(
  69827. "qname" => "foo:bar",
  69828. "namespaceUri" => "http://foo.com",
  69829. "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ),
  69830. "content" => "I'm inside the tag",
  69831. );
  69832. $multiline = true;
  69833. $indent = " ";
  69834. $linebreak = "^";
  69835. $sortAttributes = true;
  69836. $expected = "<foo:bar argh=\"fruit&amp;vegetable\"^ key=\"value\"^ xmlns:foo=\"http://foo.com\">I&apos;m inside the tag</foo:bar>";
  69837. $this->assertEquals($expected, XML_Util::createTagFromArray($original, XML_UTIL_REPLACE_ENTITIES, $multiline, $indent, $linebreak, $sortAttributes));
  69838. }
  69839. /**
  69840. * @covers XML_Util::createTagFromArray()
  69841. */
  69842. public function testCreateTagFromArrayWithQnameAndNamespaceAndAttributesAndContentWithReplaceEntitiesAndMultilineTrueAndIndentAndLinebreakAndSortAttributesFalse()
  69843. {
  69844. $original = array(
  69845. "qname" => "foo:bar",
  69846. "namespaceUri" => "http://foo.com",
  69847. "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ),
  69848. "content" => "I'm inside the tag",
  69849. );
  69850. $multiline = true;
  69851. $indent = " ";
  69852. $linebreak = "^";
  69853. $sortAttributes = false;
  69854. $expected = "<foo:bar key=\"value\"^ argh=\"fruit&amp;vegetable\"^ xmlns:foo=\"http://foo.com\">I&apos;m inside the tag</foo:bar>";
  69855. $this->assertEquals($expected, XML_Util::createTagFromArray($original, XML_UTIL_REPLACE_ENTITIES, $multiline, $indent, $linebreak, $sortAttributes));
  69856. }
  69857. /**
  69858. * @covers XML_Util::createTagFromArray()
  69859. */
  69860. public function testCreateTagFromArrayWithInvalidArray()
  69861. {
  69862. $badArray = array(
  69863. "foo" => "bar",
  69864. );
  69865. $expectedError = "You must either supply a qualified name (qname) or local tag name (localPart).";
  69866. $this->assertEquals($expectedError, XML_Util::createTagFromArray($badArray));
  69867. }
  69868. /**
  69869. * @covers XML_Util::createTagFromArray()
  69870. */
  69871. public function testCreateTagFromArrayWithNamespaceAndAttributesAndContentButWithoutQname()
  69872. {
  69873. $original = array(
  69874. "namespaceUri" => "http://foo.com",
  69875. "attributes" => array( "key" => "value", "argh" => "fruit&vegetable" ),
  69876. "content" => "I'm inside the tag",
  69877. );
  69878. $expectedError = "You must either supply a qualified name (qname) or local tag name (localPart).";
  69879. $this->assertEquals($expectedError, XML_Util::createTagFromArray($original));
  69880. }
  69881. /**
  69882. * @covers XML_Util::createTagFromArray()
  69883. */
  69884. public function testCreateTagFromArrayWithNonScalarContent()
  69885. {
  69886. $badArray = array(
  69887. 'content' => array('foo', 'bar'),
  69888. );
  69889. $expectedError = "Supplied non-scalar value as tag content";
  69890. $this->assertEquals($expectedError, XML_Util::createTagFromArray($badArray));
  69891. }
  69892. /**
  69893. * @covers XML_Util::createTagFromArray()
  69894. */
  69895. public function testCreateTagFromArrayWithArrayOfNamespaces()
  69896. {
  69897. $original = array(
  69898. 'qname' => 'foo:bar',
  69899. 'namespaces' => array('ns1' => 'uri1', 'ns2' => 'uri2'),
  69900. );
  69901. $expected = "<foo:bar xmlns:ns1=\"uri1\" xmlns:ns2=\"uri2\" />";
  69902. $this->assertEquals($expected, XML_Util::createTagFromArray($original));
  69903. }
  69904. /**
  69905. * @covers XML_Util::createTagFromArray()
  69906. */
  69907. public function testCreateTagFromArrayWithQnameDerivedFromNamespaceUriAndLocalPart()
  69908. {
  69909. $original = array(
  69910. 'namespaceUri' => 'http://bar.org',
  69911. 'localPart' => 'foo'
  69912. );
  69913. $expected = "<foo xmlns=\"http://bar.org\" />";
  69914. $this->assertEquals($expected, XML_Util::createTagFromArray($original));
  69915. }
  69916. /**
  69917. * @covers XML_Util::createTagFromArray()
  69918. */
  69919. public function testCreateTagFromArrayWithQnameDerivedFromNamespaceAndLocalPart()
  69920. {
  69921. $original = array(
  69922. 'namespace' => 'http://foo.org',
  69923. 'localPart' => 'bar'
  69924. );
  69925. $expected = "<http://foo.org:bar />";
  69926. $this->assertEquals($expected, XML_Util::createTagFromArray($original));
  69927. }
  69928. /**
  69929. * @covers XML_Util::createTagFromArray()
  69930. */
  69931. public function testCreateTagFromArrayWithQnameDerivedFromLocalPart()
  69932. {
  69933. $original = array(
  69934. 'namespace' => '',
  69935. 'localPart' => 'bar'
  69936. );
  69937. $expected = "<bar />";
  69938. $this->assertEquals($expected, XML_Util::createTagFromArray($original));
  69939. }
  69940. /**
  69941. * @covers XML_Util::createTagFromArray()
  69942. */
  69943. public function testCreateTagFromArrayWithImplicitlyEmptyContentAndCollapseNoneDoesNotCollapseTag()
  69944. {
  69945. $original = array('qname' => 'tag1');
  69946. $expected = "<tag1></tag1>";
  69947. $actual = XML_Util::createTagFromArray(
  69948. $original,
  69949. XML_UTIL_REPLACE_ENTITIES, // default $replaceEntities
  69950. false, // default $multiline
  69951. '_auto', // default $indent
  69952. "\n", // default $linebreak
  69953. true, // default $sortAttributes
  69954. XML_UTIL_COLLAPSE_NONE
  69955. );
  69956. $this->assertEquals($expected, $actual);
  69957. }
  69958. /**
  69959. * @covers XML_Util::createTagFromArray()
  69960. */
  69961. public function testCreateTagFromArrayForCdataWithExplicitlyEmptyContentDoesNotCollapseTag()
  69962. {
  69963. $original = array('qname' => 'tag1', 'content' => '');
  69964. $expected = "<tag1><![CDATA[]]></tag1>";
  69965. $this->assertEquals($expected, XML_Util::createTagFromArray($original, XML_UTIL_CDATA_SECTION));
  69966. }
  69967. }
  69968. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/GetDocTypeDeclarationTests.php�������������������������������������������������0000644�0001750�0001750�00000003363�13647063022�022111 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  69969. class GetDocTypeDeclarationTests extends AbstractUnitTests
  69970. {
  69971. /**
  69972. * @covers XML_Util::getDocTypeDeclaration()
  69973. */
  69974. public function testGetDocTypeDeclarationUsingRoot()
  69975. {
  69976. $expected = "<!DOCTYPE rootTag>";
  69977. $this->assertEquals($expected, XML_Util::getDocTypeDeclaration("rootTag"));
  69978. }
  69979. /**
  69980. * @covers XML_Util::getDocTypeDeclaration()
  69981. */
  69982. public function testGetDocTypeDeclarationUsingRootAndStringUri()
  69983. {
  69984. $expected = "<!DOCTYPE rootTag SYSTEM \"myDocType.dtd\">";
  69985. $this->assertEquals($expected, XML_Util::getDocTypeDeclaration("rootTag", "myDocType.dtd"));
  69986. }
  69987. /**
  69988. * @covers XML_Util::getDocTypeDeclaration()
  69989. */
  69990. public function testGetDocTypeDeclarationUsingRootAndArrayUri()
  69991. {
  69992. $uri = array(
  69993. 'uri' => 'http://pear.php.net/dtd/package-1.0',
  69994. 'id' => '-//PHP//PEAR/DTD PACKAGE 0.1'
  69995. );
  69996. $expected = "<!DOCTYPE rootTag PUBLIC \"-//PHP//PEAR/DTD PACKAGE 0.1\" \"http://pear.php.net/dtd/package-1.0\">";
  69997. $this->assertEquals($expected, XML_Util::getDocTypeDeclaration("rootTag", $uri));
  69998. }
  69999. /**
  70000. * @covers XML_Util::getDocTypeDeclaration()
  70001. */
  70002. public function testGetDocTypeDeclarationUsingRootAndArrayUriAndInternalDtd()
  70003. {
  70004. $uri = array(
  70005. 'uri' => 'http://pear.php.net/dtd/package-1.0',
  70006. 'id' => '-//PHP//PEAR/DTD PACKAGE 0.1'
  70007. );
  70008. $dtdEntry = '<!ELEMENT additionalInfo (#PCDATA)>';
  70009. $expected =
  70010. <<< EOF
  70011. <!DOCTYPE rootTag PUBLIC "-//PHP//PEAR/DTD PACKAGE 0.1" "http://pear.php.net/dtd/package-1.0" [
  70012. <!ELEMENT additionalInfo (#PCDATA)>
  70013. ]>
  70014. EOF;
  70015. $this->assertEquals($expected, XML_Util::getDocTypeDeclaration("rootTag", $uri, $dtdEntry));
  70016. }
  70017. }
  70018. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/GetXmlDeclarationTests.php�����������������������������������������������������0000644�0001750�0001750�00000002214�13647063022�021274 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  70019. class GetXMLDeclarationTests extends AbstractUnitTests
  70020. {
  70021. /**
  70022. * @covers XML_Util::getXMLDeclaration()
  70023. */
  70024. public function testGetXMLDeclarationUsingVersion()
  70025. {
  70026. $version = "1.0";
  70027. $expected = "<?xml version=\"1.0\"?>";
  70028. $this->assertEquals($expected, XML_Util::getXMLDeclaration($version));
  70029. }
  70030. /**
  70031. * @covers XML_Util::getXMLDeclaration()
  70032. */
  70033. public function testGetXMLDeclarationUsingVersionAndEncodingAndStandalone()
  70034. {
  70035. $version = "1.0";
  70036. $encoding = "UTF-8";
  70037. $standalone = true;
  70038. $expected = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
  70039. $this->assertEquals($expected, XML_Util::getXMLDeclaration($version, $encoding, $standalone));
  70040. }
  70041. /**
  70042. * @covers XML_Util::getXMLDeclaration()
  70043. */
  70044. public function testGetXMLDeclarationUsingVersionAndStandalone()
  70045. {
  70046. $version = "1.0";
  70047. $encoding = null;
  70048. $standalone = true;
  70049. $expected = "<?xml version=\"1.0\" standalone=\"yes\"?>";
  70050. $this->assertEquals($expected, XML_Util::getXMLDeclaration($version, $encoding, $standalone));
  70051. }
  70052. }
  70053. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/IsValidNameTests.php�����������������������������������������������������������0000644�0001750�0001750�00000003677�13647063022�020100 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  70054. class IsValidNameTests extends AbstractUnitTests
  70055. {
  70056. /**
  70057. * @covers XML_Util::isValidName()
  70058. */
  70059. public function testIsValidNameForTagNameThatIsValid()
  70060. {
  70061. $tagName = "alpha-x_y_z.123";
  70062. $result = XML_Util::isValidName($tagName);
  70063. $this->assertTrue($result);
  70064. }
  70065. /**
  70066. * @covers XML_Util::isValidName()
  70067. */
  70068. public function testIsValidNameForTagNameWithInvalidCharacter()
  70069. {
  70070. $tagName = "invalidTag?";
  70071. $result = XML_Util::isValidName($tagName);
  70072. $this->assertInstanceOf('PEAR_Error', $result);
  70073. $expectedError = "XML names may only contain alphanumeric chars, period, hyphen, colon and underscores";
  70074. $this->assertEquals($expectedError, $result->getMessage());
  70075. }
  70076. /**
  70077. * @covers XML_Util::isValidName()
  70078. */
  70079. public function testIsValidNameForTagNameWithInvalidStartingCharacter()
  70080. {
  70081. $tagName = "1234five";
  70082. $result = XML_Util::isValidName($tagName);
  70083. $this->assertInstanceOf('PEAR_Error', $result);
  70084. $expectedError = "XML names may only start with letter or underscore";
  70085. $this->assertEquals($expectedError, $result->getMessage());
  70086. }
  70087. /**
  70088. * @covers XML_Util::isValidName()
  70089. */
  70090. public function testIsValidNameForInt()
  70091. {
  70092. $tagName = 1;
  70093. $result = XML_Util::isValidName($tagName);
  70094. $this->assertInstanceOf('PEAR_Error', $result);
  70095. $expectedError = "XML names may only start with letter or underscore";
  70096. $this->assertEquals($expectedError, $result->getMessage());
  70097. }
  70098. /**
  70099. * @covers XML_Util::isValidName()
  70100. */
  70101. public function testIsValidNameForEmptyString()
  70102. {
  70103. $tagName = '';
  70104. $result = XML_Util::isValidName($tagName);
  70105. $this->assertInstanceOf('PEAR_Error', $result);
  70106. $expectedError = "XML names may only start with letter or underscore";
  70107. $this->assertEquals($expectedError, $result->getMessage());
  70108. }
  70109. }
  70110. �����������������������������������������������������������������XML_Util-1.4.5/tests/RaiseErrorTests.php������������������������������������������������������������0000644�0001750�0001750�00000000700�13647063022�020001 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  70111. class RaiseErrorTests extends AbstractUnitTests
  70112. {
  70113. /**
  70114. * @covers XML_Util::raiseError()
  70115. */
  70116. public function testRaiseError()
  70117. {
  70118. $code = 12345;
  70119. $message = "I am an error";
  70120. $error = XML_Util::raiseError($message, $code);
  70121. $this->assertInstanceOf('PEAR_Error', $error);
  70122. $this->assertEquals($message, $error->getMessage());
  70123. $this->assertEquals($code, $error->getCode());
  70124. }
  70125. }
  70126. ����������������������������������������������������������������XML_Util-1.4.5/tests/ReplaceEntitiesTests.php�������������������������������������������������������0000644�0001750�0001750�00000010341�13647063022�021006 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  70127. class ReplaceEntitiesTests extends AbstractUnitTests
  70128. {
  70129. protected function getSimpleData()
  70130. {
  70131. return 'This string contains < & >.';
  70132. }
  70133. protected function getUtf8Data()
  70134. {
  70135. return 'This data contains special chars like <, >, & and " as well as ä, ö, ß, à and ê';
  70136. }
  70137. /**
  70138. * @covers XML_Util::replaceEntities()
  70139. */
  70140. public function testReplaceEntitiesForSimpleData()
  70141. {
  70142. $expected = "This string contains &lt; &amp; &gt;.";
  70143. $this->assertEquals($expected, XML_Util::replaceEntities($this->getSimpleData()));
  70144. }
  70145. /**
  70146. * @covers XML_Util::replaceEntities()
  70147. */
  70148. public function testReplaceEntitiesForSimpleDataWithInvalidOptionReturnsOriginalData()
  70149. {
  70150. $expected = "This string contains < & >.";
  70151. $this->assertEquals($expected, XML_Util::replaceEntities($this->getSimpleData(), 'INVALID_OPTION'));
  70152. }
  70153. /**
  70154. * @covers XML_Util::replaceEntities()
  70155. */
  70156. public function testReplaceEntitiesForSimpleDataWithEntitiesXml()
  70157. {
  70158. $expected = "This string contains &lt; &amp; &gt;.";
  70159. $this->assertEquals($expected, XML_Util::replaceEntities($this->getSimpleData(), XML_UTIL_ENTITIES_XML));
  70160. }
  70161. /**
  70162. * @covers XML_Util::replaceEntities()
  70163. */
  70164. public function testReplaceEntitiesForSimpleDataWithEntitiesXmlAndEncoding()
  70165. {
  70166. $encoding = "UTF-8";
  70167. $expected = "This string contains &lt; &amp; &gt;.";
  70168. $this->assertEquals($expected, XML_Util::replaceEntities($this->getSimpleData(), XML_UTIL_ENTITIES_XML, $encoding));
  70169. }
  70170. /**
  70171. * @covers XML_Util::replaceEntities()
  70172. */
  70173. public function testReplaceEntitiesForUtf8DataWithEntitiesXmlAndEncoding()
  70174. {
  70175. $encoding = "UTF-8";
  70176. $expected = "This data contains special chars like &lt;, &gt;, &amp; and &quot; as well as ä, ö, ß, à and ê";
  70177. $this->assertEquals($expected, XML_Util::replaceEntities($this->getUtf8Data(), XML_UTIL_ENTITIES_XML, $encoding));
  70178. }
  70179. /**
  70180. * @covers XML_Util::replaceEntities()
  70181. */
  70182. public function testReplaceEntitiesForSimpleDataWithEntitiesXmlRequired()
  70183. {
  70184. $expected = "This string contains &lt; &amp; >.";
  70185. $this->assertEquals($expected, XML_Util::replaceEntities($this->getSimpleData(), XML_UTIL_ENTITIES_XML_REQUIRED));
  70186. }
  70187. /**
  70188. * @covers XML_Util::replaceEntities()
  70189. */
  70190. public function testReplaceEntitiesForSimpleDataWithEntitiesXmlRequiredAndEncoding()
  70191. {
  70192. $encoding = "UTF-8";
  70193. $expected = "This string contains &lt; &amp; >.";
  70194. $this->assertEquals($expected, XML_Util::replaceEntities($this->getSimpleData(), XML_UTIL_ENTITIES_XML_REQUIRED, $encoding));
  70195. }
  70196. /**
  70197. * @covers XML_Util::replaceEntities()
  70198. */
  70199. public function testReplaceEntitiesForUtf8DataWithEntitiesXmlRequiredAndEncoding()
  70200. {
  70201. $encoding = "UTF-8";
  70202. $expected = "This data contains special chars like &lt;, >, &amp; and &quot; as well as ä, ö, ß, à and ê";
  70203. $this->assertEquals($expected, XML_Util::replaceEntities($this->getUtf8Data(), XML_UTIL_ENTITIES_XML_REQUIRED, $encoding));
  70204. }
  70205. /**
  70206. * @covers XML_Util::replaceEntities()
  70207. */
  70208. public function testReplaceEntitiesForSimpleDataWithEntitiesHtml()
  70209. {
  70210. $expected = "This string contains &lt; &amp; &gt;.";
  70211. $this->assertEquals($expected, XML_Util::replaceEntities($this->getSimpleData(), XML_UTIL_ENTITIES_HTML));
  70212. }
  70213. /**
  70214. * @covers XML_Util::replaceEntities()
  70215. */
  70216. public function testReplaceEntitiesForSimpleDataWithEntitiesHtmlAndEncoding()
  70217. {
  70218. $encoding = "UTF-8";
  70219. $expected = "This string contains &lt; &amp; &gt;.";
  70220. $this->assertEquals($expected, XML_Util::replaceEntities($this->getSimpleData(), XML_UTIL_ENTITIES_HTML, $encoding));
  70221. }
  70222. /**
  70223. * @covers XML_Util::replaceEntities()
  70224. */
  70225. public function testReplaceEntitiesForUtf8DataWithEntitiesHtmlAndEncoding()
  70226. {
  70227. $encoding = "UTF-8";
  70228. $expected = "This data contains special chars like &lt;, &gt;, &amp; and &quot; as well as &auml;, &ouml;, &szlig;, &agrave; and &ecirc;";
  70229. $this->assertEquals($expected, XML_Util::replaceEntities($this->getUtf8Data(), XML_UTIL_ENTITIES_HTML, $encoding));
  70230. }
  70231. }
  70232. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/ReverseEntitiesTests.php�������������������������������������������������������0000644�0001750�0001750�00000010326�13647063022�021051 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  70233. class ReverseEntitiesTests extends AbstractUnitTests
  70234. {
  70235. protected function getSimpleData()
  70236. {
  70237. return 'This string contains &lt; &amp; &gt;.';
  70238. }
  70239. protected function getUtf8Data()
  70240. {
  70241. return 'This data contains special chars like &lt;, &gt;, &amp; and &quot; as well as &auml;, &ouml;, &szlig;, &agrave; and &ecirc;';
  70242. }
  70243. /**
  70244. * @covers XML_Util::reverseEntities()
  70245. */
  70246. public function testReverseEntitiesForSimpleData()
  70247. {
  70248. $expected = "This string contains < & >.";
  70249. $this->assertEquals($expected, XML_Util::reverseEntities($this->getSimpleData()));
  70250. }
  70251. /**
  70252. * @covers XML_Util::reverseEntities()
  70253. */
  70254. public function testReverseEntitiesForSimpleDataWithInvalidOptionReturnsOriginalData()
  70255. {
  70256. $expected = "This string contains &lt; &amp; &gt;.";
  70257. $this->assertEquals($expected, XML_Util::reverseEntities($this->getSimpleData(), 'INVALID_OPTION'));
  70258. }
  70259. /**
  70260. * @covers XML_Util::reverseEntities()
  70261. */
  70262. public function testReverseEntitiesForSimpleDataWithEntitiesXml()
  70263. {
  70264. $expected = "This string contains < & >.";
  70265. $this->assertEquals($expected, XML_Util::reverseEntities($this->getSimpleData(), XML_UTIL_ENTITIES_XML));
  70266. }
  70267. /**
  70268. * @covers XML_Util::reverseEntities()
  70269. */
  70270. public function testReverseEntitiesForSimpleDataWithEntitiesXmlAndEncoding()
  70271. {
  70272. $encoding = "UTF-8";
  70273. $expected = "This string contains < & >.";
  70274. $this->assertEquals($expected, XML_Util::reverseEntities($this->getSimpleData(), XML_UTIL_ENTITIES_XML), $encoding);
  70275. }
  70276. /**
  70277. * @covers XML_Util::reverseEntities()
  70278. */
  70279. public function testReverseEntitiesForUtf8DataWithEntitiesXmlAndEncoding()
  70280. {
  70281. $encoding = "UTF-8";
  70282. $expected = "This data contains special chars like <, >, & and \" as well as &auml;, &ouml;, &szlig;, &agrave; and &ecirc;";
  70283. $this->assertEquals($expected, XML_Util::reverseEntities($this->getUtf8Data(), XML_UTIL_ENTITIES_XML), $encoding);
  70284. }
  70285. /**
  70286. * @covers XML_Util::reverseEntities()
  70287. */
  70288. public function testReverseEntitiesForSimpleDataWithEntitiesXmlRequired()
  70289. {
  70290. $expected = "This string contains < & &gt;.";
  70291. $this->assertEquals($expected, XML_Util::reverseEntities($this->getSimpleData(), XML_UTIL_ENTITIES_XML_REQUIRED));
  70292. }
  70293. /**
  70294. * @covers XML_Util::reverseEntities()
  70295. */
  70296. public function testReverseEntitiesForSimpleDataWithEntitiesXmlRequiredAndEncoding()
  70297. {
  70298. $encoding = "UTF-8";
  70299. $expected = "This string contains < & &gt;.";
  70300. $this->assertEquals($expected, XML_Util::reverseEntities($this->getSimpleData(), XML_UTIL_ENTITIES_XML_REQUIRED, $encoding));
  70301. }
  70302. /**
  70303. * @covers XML_Util::reverseEntities()
  70304. */
  70305. public function testReverseEntitiesForUtf8DataWithEntitiesXmlRequiredAndEncoding()
  70306. {
  70307. $encoding = "UTF-8";
  70308. $expected = "This data contains special chars like <, &gt;, & and \" as well as &auml;, &ouml;, &szlig;, &agrave; and &ecirc;";
  70309. $this->assertEquals($expected, XML_Util::reverseEntities($this->getUtf8Data(), XML_UTIL_ENTITIES_XML_REQUIRED, $encoding));
  70310. }
  70311. /**
  70312. * @covers XML_Util::reverseEntities()
  70313. */
  70314. public function testReverseEntitiesForSimpleDataWithEntitiesHtml()
  70315. {
  70316. $expected = "This string contains < & >.";
  70317. $this->assertEquals($expected, XML_Util::reverseEntities($this->getSimpleData(), XML_UTIL_ENTITIES_HTML));
  70318. }
  70319. /**
  70320. * @covers XML_Util::reverseEntities()
  70321. */
  70322. public function testReverseEntitiesForSimpleDataWithEntitiesHtmlAndEncoding()
  70323. {
  70324. $encoding = "UTF-8";
  70325. $expected = "This string contains < & >.";
  70326. $this->assertEquals($expected, XML_Util::reverseEntities($this->getSimpleData(), XML_UTIL_ENTITIES_HTML, $encoding));
  70327. }
  70328. /**
  70329. * @covers XML_Util::reverseEntities()
  70330. */
  70331. public function testReverseEntitiesForUtf8DataWithEntitiesHtmlAndEncoding()
  70332. {
  70333. $encoding = "UTF-8";
  70334. $expected = "This data contains special chars like <, >, & and \" as well as ä, ö, ß, à and ê";
  70335. $this->assertEquals($expected, XML_Util::reverseEntities($this->getUtf8Data(), XML_UTIL_ENTITIES_HTML, $encoding));
  70336. }
  70337. }
  70338. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/SplitQualifiedNameTests.php����������������������������������������������������0000644�0001750�0001750�00000001507�13647063022�021452 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  70339. class SplitQualifiedNameTests extends AbstractUnitTests
  70340. {
  70341. /**
  70342. * @covers XML_Util::splitQualifiedName()
  70343. */
  70344. public function testSplitQualifiedNameWithoutNamespace()
  70345. {
  70346. $original = "xslt:stylesheet";
  70347. $expected = array(
  70348. 'namespace' => 'xslt',
  70349. 'localPart' => 'stylesheet',
  70350. );
  70351. $this->assertEquals($expected, XML_Util::splitQualifiedName($original));
  70352. }
  70353. /**
  70354. * @covers XML_Util::splitQualifiedName()
  70355. */
  70356. public function testSplitQualifiedNameWithNamespace()
  70357. {
  70358. $original = "stylesheet";
  70359. $namespace = "myNs";
  70360. $expected = array(
  70361. 'namespace' => 'myNs',
  70362. 'localPart' => 'stylesheet',
  70363. );
  70364. $this->assertEquals($expected, XML_Util::splitQualifiedName($original, $namespace));
  70365. }
  70366. }
  70367. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/Bug4950Tests.php���������������������������������������������������������������0000644�0001750�0001750�00000001331�13647063022�016764 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  70368. /**
  70369. * Bug #4950 "Incorrect CDATA serializing"
  70370. *
  70371. * Content that looks like CDATA end characters and tags
  70372. * should still be recognized solely as content text.
  70373. *
  70374. * @link https://pear.php.net/bugs/bug.php?id=4950
  70375. */
  70376. class Bug4950Tests extends AbstractUnitTests
  70377. {
  70378. public function testCreateTagForBug4950()
  70379. {
  70380. $qname = "test";
  70381. $attributes = array();
  70382. $content = "Content ]]></test> here!";
  70383. $namespaceUrl = null;
  70384. $expected = "<test><![CDATA[Content ]]]]><![CDATA[></test> here!]]></test>";
  70385. $result = XML_Util::createTag($qname, $attributes, $content, $namespaceUrl, XML_UTIL_CDATA_SECTION);
  70386. $this->assertEquals($expected, $result, "Failed bugcheck.");
  70387. }
  70388. }
  70389. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/Bug5392Tests.php���������������������������������������������������������������0000644�0001750�0001750�00000001377�13647063022�016777 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  70390. /**
  70391. * Bug #5392 "encoding of ISO-8859-1 is the only supported encoding"
  70392. *
  70393. * Original characters of the given encoding that are "replaced"
  70394. * should then "reverse" back to perfectly match the original.
  70395. *
  70396. * @link https://pear.php.net/bugs/bug.php?id=5392
  70397. */
  70398. class Bug5392Tests extends AbstractUnitTests
  70399. {
  70400. public function testReplaceEntitiesForBug5392()
  70401. {
  70402. $original = 'This data contains special chars like <, >, & and " as well as ä, ö, ß, à and ê';
  70403. $replacedResult = XML_Util::replaceEntities($original, XML_UTIL_ENTITIES_HTML, "UTF-8");
  70404. $reversedResult = XML_Util::reverseEntities($replacedResult, XML_UTIL_ENTITIES_HTML, "UTF-8");
  70405. $this->assertEquals($original, $reversedResult, "Failed bugcheck.");
  70406. }
  70407. }
  70408. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/Bug18343Tests.php��������������������������������������������������������������0000644�0001750�0001750�00000003316�13647063022�017052 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  70409. /**
  70410. * Bug #18343 "Entities in file names decoded during packaging"
  70411. *
  70412. * No matter what flags are given to createTagFromArray(),
  70413. * an attribute must *always* be at least ENTITIES_XML encoded.
  70414. *
  70415. * @link https://pear.php.net/bugs/bug.php?id=18343
  70416. */
  70417. class Bug18343Tests extends AbstractUnitTests
  70418. {
  70419. private $tagArray = array(
  70420. "qname" => "install",
  70421. "attributes" => array(
  70422. "as" => "Horde/Feed/fixtures/lexicon/http-p.moreover.com-cgi-local-page%2Fo=rss&s=Newsweek",
  70423. "name" => "test/Horde/Feed/fixtures/lexicon/http-p.moreover.com-cgi-local-page%2Fo=rss&s=Newsweek",
  70424. )
  70425. );
  70426. public function getFlagsToTest()
  70427. {
  70428. new XML_Util(); // for constants to be declared
  70429. return array(
  70430. array('no flag', null),
  70431. array('false', false),
  70432. array('ENTITIES_NONE', XML_UTIL_ENTITIES_NONE),
  70433. array('ENTITIES_XML', XML_UTIL_ENTITIES_XML),
  70434. array('ENTITIES_XML_REQUIRED', XML_UTIL_ENTITIES_XML_REQUIRED),
  70435. array('ENTITIES_HTML', XML_UTIL_ENTITIES_HTML),
  70436. array('REPLACE_ENTITIES', XML_UTIL_REPLACE_ENTITIES),
  70437. );
  70438. }
  70439. /**
  70440. * @dataProvider getFlagsToTest()
  70441. */
  70442. public function testCreateTagFromArrayForBug18343($key, $flag)
  70443. {
  70444. // all flags for the candidate input should return the same result
  70445. $expected =
  70446. <<< EOF
  70447. <install as="Horde/Feed/fixtures/lexicon/http-p.moreover.com-cgi-local-page%2Fo=rss&amp;s=Newsweek" name="test/Horde/Feed/fixtures/lexicon/http-p.moreover.com-cgi-local-page%2Fo=rss&amp;s=Newsweek" />
  70448. EOF;
  70449. $this->assertEquals($expected, XML_Util::createTagFromArray($this->tagArray, $flag), "Failed bugcheck for $key.");
  70450. }
  70451. }
  70452. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/Bug21177Tests.php��������������������������������������������������������������0000644�0001750�0001750�00000002036�13647063022�017047 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  70453. /**
  70454. * Bug #21177 "XML_Util::collapseEmptyTags() can return NULL"
  70455. *
  70456. * PREG returns NULL when it encounters an error.
  70457. * In this case, it was encountering PREG_BACKTRACK_LIMIT_ERROR.
  70458. *
  70459. * @link https://pear.php.net/bugs/bug.php?id=21177
  70460. */
  70461. class Bug21177Tests extends AbstractUnitTests
  70462. {
  70463. public function getTestCandidate()
  70464. {
  70465. $expected = '<id_mytest_yesorno />';
  70466. return array(
  70467. array('<idmytestyesorno></idmytestyesorno>', '<idmytestyesorno />'),
  70468. array('<idmytestyesorno />', '<idmytestyesorno />'),
  70469. array('<id_mytest_yesorno></id_mytest_yesorno>', '<id_mytest_yesorno />'),
  70470. array('<id_mytest_yesorno />', '<id_mytest_yesorno />'),
  70471. );
  70472. }
  70473. /**
  70474. * @dataProvider getTestCandidate()
  70475. */
  70476. public function testCollapseEmptyTagsForBug21177($original, $expected)
  70477. {
  70478. $this->assertEquals($expected, XML_Util::collapseEmptyTags($original, XML_UTIL_COLLAPSE_ALL), "Failed bugcheck.");
  70479. }
  70480. }
  70481. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������XML_Util-1.4.5/tests/Bug21184Tests.php��������������������������������������������������������������0000644�0001750�0001750�00000000702�13647063022�017043 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  70482. /**
  70483. * Bug #21184
  70484. *
  70485. * PREG returns NULL when it encounters an error.
  70486. * In this case, it was encountering PREG_BACKTRACK_LIMIT_ERROR.
  70487. *
  70488. * @link https://pear.php.net/bugs/bug.php?id=21177
  70489. */
  70490. class Bug21184 extends AbstractUnitTests
  70491. {
  70492. public function testBug21184()
  70493. {
  70494. $xml = '<XML_Serializer_Tag>one</XML_Serializer_Tag>';
  70495. $this->assertEquals($xml, XML_Util::collapseEmptyTags($xml, XML_UTIL_COLLAPSE_ALL));
  70496. }
  70497. }
  70498. ��������������������������������������������������������������XML_Util-1.4.5/XML/Util.php�������������������������������������������������������������������������0000644�0001750�0001750�00000076660�13647063022�015136 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  70499. /**
  70500. * XML_Util
  70501. *
  70502. * XML Utilities package
  70503. *
  70504. * PHP versions 4 and 5
  70505. *
  70506. * LICENSE:
  70507. *
  70508. * Copyright (c) 2003-2008 Stephan Schmidt <schst@php.net>
  70509. * All rights reserved.
  70510. *
  70511. * Redistribution and use in source and binary forms, with or without
  70512. * modification, are permitted provided that the following conditions
  70513. * are met:
  70514. *
  70515. * * Redistributions of source code must retain the above copyright
  70516. * notice, this list of conditions and the following disclaimer.
  70517. * * Redistributions in binary form must reproduce the above copyright
  70518. * notice, this list of conditions and the following disclaimer in the
  70519. * documentation and/or other materials provided with the distribution.
  70520. * * The name of the author may not be used to endorse or promote products
  70521. * derived from this software without specific prior written permission.
  70522. *
  70523. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  70524. * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  70525. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  70526. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  70527. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  70528. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  70529. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  70530. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  70531. * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  70532. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  70533. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  70534. *
  70535. * @category XML
  70536. * @package XML_Util
  70537. * @author Stephan Schmidt <schst@php.net>
  70538. * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
  70539. * @license http://opensource.org/licenses/bsd-license New BSD License
  70540. * @version CVS: $Id$
  70541. * @link http://pear.php.net/package/XML_Util
  70542. */
  70543. /**
  70544. * Error code for invalid chars in XML name
  70545. */
  70546. define('XML_UTIL_ERROR_INVALID_CHARS', 51);
  70547. /**
  70548. * Error code for invalid chars in XML name
  70549. */
  70550. define('XML_UTIL_ERROR_INVALID_START', 52);
  70551. /**
  70552. * Error code for non-scalar tag content
  70553. */
  70554. define('XML_UTIL_ERROR_NON_SCALAR_CONTENT', 60);
  70555. /**
  70556. * Error code for missing tag name
  70557. */
  70558. define('XML_UTIL_ERROR_NO_TAG_NAME', 61);
  70559. /**
  70560. * Replace XML entities
  70561. */
  70562. define('XML_UTIL_REPLACE_ENTITIES', 1);
  70563. /**
  70564. * Embedd content in a CData Section
  70565. */
  70566. define('XML_UTIL_CDATA_SECTION', 5);
  70567. /**
  70568. * Do not replace entitites
  70569. */
  70570. define('XML_UTIL_ENTITIES_NONE', 0);
  70571. /**
  70572. * Replace all XML entitites
  70573. * This setting will replace <, >, ", ' and &
  70574. */
  70575. define('XML_UTIL_ENTITIES_XML', 1);
  70576. /**
  70577. * Replace only required XML entitites
  70578. * This setting will replace <, " and &
  70579. */
  70580. define('XML_UTIL_ENTITIES_XML_REQUIRED', 2);
  70581. /**
  70582. * Replace HTML entitites
  70583. * @link http://www.php.net/htmlentities
  70584. */
  70585. define('XML_UTIL_ENTITIES_HTML', 3);
  70586. /**
  70587. * Do not collapse any empty tags.
  70588. */
  70589. define('XML_UTIL_COLLAPSE_NONE', 0);
  70590. /**
  70591. * Collapse all empty tags.
  70592. */
  70593. define('XML_UTIL_COLLAPSE_ALL', 1);
  70594. /**
  70595. * Collapse only empty XHTML tags that have no end tag.
  70596. */
  70597. define('XML_UTIL_COLLAPSE_XHTML_ONLY', 2);
  70598. /**
  70599. * Utility class for working with XML documents
  70600. *
  70601. * @category XML
  70602. * @package XML_Util
  70603. * @author Stephan Schmidt <schst@php.net>
  70604. * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
  70605. * @license http://opensource.org/licenses/bsd-license New BSD License
  70606. * @version Release: 1.4.5
  70607. * @link http://pear.php.net/package/XML_Util
  70608. */
  70609. class XML_Util
  70610. {
  70611. /**
  70612. * Return API version
  70613. *
  70614. * @return string $version API version
  70615. */
  70616. public static function apiVersion()
  70617. {
  70618. return '1.4';
  70619. }
  70620. /**
  70621. * Replace XML entities
  70622. *
  70623. * With the optional second parameter, you may select, which
  70624. * entities should be replaced.
  70625. *
  70626. * <code>
  70627. * require_once 'XML/Util.php';
  70628. *
  70629. * // replace XML entites:
  70630. * $string = XML_Util::replaceEntities('This string contains < & >.');
  70631. * </code>
  70632. *
  70633. * With the optional third parameter, you may pass the character encoding
  70634. * <code>
  70635. * require_once 'XML/Util.php';
  70636. *
  70637. * // replace XML entites in UTF-8:
  70638. * $string = XML_Util::replaceEntities(
  70639. * 'This string contains < & > as well as ä, ö, ß, à and ê',
  70640. * XML_UTIL_ENTITIES_HTML,
  70641. * 'UTF-8'
  70642. * );
  70643. * </code>
  70644. *
  70645. * @param string $string string where XML special chars
  70646. * should be replaced
  70647. * @param int $replaceEntities setting for entities in attribute values
  70648. * (one of XML_UTIL_ENTITIES_XML,
  70649. * XML_UTIL_ENTITIES_XML_REQUIRED,
  70650. * XML_UTIL_ENTITIES_HTML)
  70651. * @param string $encoding encoding value (if any)...
  70652. * must be a valid encoding as determined
  70653. * by the htmlentities() function
  70654. *
  70655. * @return string string with replaced chars
  70656. * @see reverseEntities()
  70657. */
  70658. public static function replaceEntities(
  70659. $string, $replaceEntities = XML_UTIL_ENTITIES_XML, $encoding = 'ISO-8859-1'
  70660. ) {
  70661. switch ($replaceEntities) {
  70662. case XML_UTIL_ENTITIES_XML:
  70663. return strtr(
  70664. $string,
  70665. array(
  70666. '&' => '&amp;',
  70667. '>' => '&gt;',
  70668. '<' => '&lt;',
  70669. '"' => '&quot;',
  70670. '\'' => '&apos;'
  70671. )
  70672. );
  70673. break;
  70674. case XML_UTIL_ENTITIES_XML_REQUIRED:
  70675. return strtr(
  70676. $string,
  70677. array(
  70678. '&' => '&amp;',
  70679. '<' => '&lt;',
  70680. '"' => '&quot;'
  70681. )
  70682. );
  70683. break;
  70684. case XML_UTIL_ENTITIES_HTML:
  70685. return htmlentities($string, ENT_COMPAT, $encoding);
  70686. break;
  70687. }
  70688. return $string;
  70689. }
  70690. /**
  70691. * Reverse XML entities
  70692. *
  70693. * With the optional second parameter, you may select, which
  70694. * entities should be reversed.
  70695. *
  70696. * <code>
  70697. * require_once 'XML/Util.php';
  70698. *
  70699. * // reverse XML entites:
  70700. * $string = XML_Util::reverseEntities('This string contains &lt; &amp; &gt;.');
  70701. * </code>
  70702. *
  70703. * With the optional third parameter, you may pass the character encoding
  70704. * <code>
  70705. * require_once 'XML/Util.php';
  70706. *
  70707. * // reverse XML entites in UTF-8:
  70708. * $string = XML_Util::reverseEntities(
  70709. * 'This string contains &lt; &amp; &gt; as well as'
  70710. * . ' &auml;, &ouml;, &szlig;, &agrave; and &ecirc;',
  70711. * XML_UTIL_ENTITIES_HTML,
  70712. * 'UTF-8'
  70713. * );
  70714. * </code>
  70715. *
  70716. * @param string $string string where XML special chars
  70717. * should be replaced
  70718. * @param int $replaceEntities setting for entities in attribute values
  70719. * (one of XML_UTIL_ENTITIES_XML,
  70720. * XML_UTIL_ENTITIES_XML_REQUIRED,
  70721. * XML_UTIL_ENTITIES_HTML)
  70722. * @param string $encoding encoding value (if any)...
  70723. * must be a valid encoding as determined
  70724. * by the html_entity_decode() function
  70725. *
  70726. * @return string string with replaced chars
  70727. * @see replaceEntities()
  70728. */
  70729. public static function reverseEntities(
  70730. $string, $replaceEntities = XML_UTIL_ENTITIES_XML, $encoding = 'ISO-8859-1'
  70731. ) {
  70732. switch ($replaceEntities) {
  70733. case XML_UTIL_ENTITIES_XML:
  70734. return strtr(
  70735. $string,
  70736. array(
  70737. '&amp;' => '&',
  70738. '&gt;' => '>',
  70739. '&lt;' => '<',
  70740. '&quot;' => '"',
  70741. '&apos;' => '\''
  70742. )
  70743. );
  70744. break;
  70745. case XML_UTIL_ENTITIES_XML_REQUIRED:
  70746. return strtr(
  70747. $string,
  70748. array(
  70749. '&amp;' => '&',
  70750. '&lt;' => '<',
  70751. '&quot;' => '"'
  70752. )
  70753. );
  70754. break;
  70755. case XML_UTIL_ENTITIES_HTML:
  70756. return html_entity_decode($string, ENT_COMPAT, $encoding);
  70757. break;
  70758. }
  70759. return $string;
  70760. }
  70761. /**
  70762. * Build an xml declaration
  70763. *
  70764. * <code>
  70765. * require_once 'XML/Util.php';
  70766. *
  70767. * // get an XML declaration:
  70768. * $xmlDecl = XML_Util::getXMLDeclaration('1.0', 'UTF-8', true);
  70769. * </code>
  70770. *
  70771. * @param string $version xml version
  70772. * @param string $encoding character encoding
  70773. * @param bool $standalone document is standalone (or not)
  70774. *
  70775. * @return string xml declaration
  70776. * @uses attributesToString() to serialize the attributes of the
  70777. * XML declaration
  70778. */
  70779. public static function getXMLDeclaration(
  70780. $version = '1.0', $encoding = null, $standalone = null
  70781. ) {
  70782. $attributes = array(
  70783. 'version' => $version,
  70784. );
  70785. // add encoding
  70786. if ($encoding !== null) {
  70787. $attributes['encoding'] = $encoding;
  70788. }
  70789. // add standalone, if specified
  70790. if ($standalone !== null) {
  70791. $attributes['standalone'] = $standalone ? 'yes' : 'no';
  70792. }
  70793. return sprintf(
  70794. '<?xml%s?>',
  70795. XML_Util::attributesToString($attributes, false)
  70796. );
  70797. }
  70798. /**
  70799. * Build a document type declaration
  70800. *
  70801. * <code>
  70802. * require_once 'XML/Util.php';
  70803. *
  70804. * // get a doctype declaration:
  70805. * $xmlDecl = XML_Util::getDocTypeDeclaration('rootTag','myDocType.dtd');
  70806. * </code>
  70807. *
  70808. * @param string $root name of the root tag
  70809. * @param string $uri uri of the doctype definition
  70810. * (or array with uri and public id)
  70811. * @param string $internalDtd internal dtd entries
  70812. *
  70813. * @return string doctype declaration
  70814. * @since 0.2
  70815. */
  70816. public static function getDocTypeDeclaration(
  70817. $root, $uri = null, $internalDtd = null
  70818. ) {
  70819. if (is_array($uri)) {
  70820. $ref = sprintf(' PUBLIC "%s" "%s"', $uri['id'], $uri['uri']);
  70821. } elseif (!empty($uri)) {
  70822. $ref = sprintf(' SYSTEM "%s"', $uri);
  70823. } else {
  70824. $ref = '';
  70825. }
  70826. if (empty($internalDtd)) {
  70827. return sprintf('<!DOCTYPE %s%s>', $root, $ref);
  70828. } else {
  70829. return sprintf("<!DOCTYPE %s%s [\n%s\n]>", $root, $ref, $internalDtd);
  70830. }
  70831. }
  70832. /**
  70833. * Create string representation of an attribute list
  70834. *
  70835. * <code>
  70836. * require_once 'XML/Util.php';
  70837. *
  70838. * // build an attribute string
  70839. * $att = array(
  70840. * 'foo' => 'bar',
  70841. * 'argh' => 'tomato'
  70842. * );
  70843. *
  70844. * $attList = XML_Util::attributesToString($att);
  70845. * </code>
  70846. *
  70847. * @param array $attributes attribute array
  70848. * @param bool|array $sort sort attribute list alphabetically,
  70849. * may also be an assoc array containing
  70850. * the keys 'sort', 'multiline', 'indent',
  70851. * 'linebreak' and 'entities'
  70852. * @param bool $multiline use linebreaks, if more than
  70853. * one attribute is given
  70854. * @param string $indent string used for indentation of
  70855. * multiline attributes
  70856. * @param string $linebreak string used for linebreaks of
  70857. * multiline attributes
  70858. * @param int $entities setting for entities in attribute values
  70859. * (one of XML_UTIL_ENTITIES_NONE,
  70860. * XML_UTIL_ENTITIES_XML,
  70861. * XML_UTIL_ENTITIES_XML_REQUIRED,
  70862. * XML_UTIL_ENTITIES_HTML)
  70863. *
  70864. * @return string string representation of the attributes
  70865. * @uses replaceEntities() to replace XML entities in attribute values
  70866. * @todo allow sort also to be an options array
  70867. */
  70868. public static function attributesToString(
  70869. $attributes, $sort = true, $multiline = false,
  70870. $indent = ' ', $linebreak = "\n", $entities = XML_UTIL_ENTITIES_XML
  70871. ) {
  70872. /*
  70873. * second parameter may be an array
  70874. */
  70875. if (is_array($sort)) {
  70876. if (isset($sort['multiline'])) {
  70877. $multiline = $sort['multiline'];
  70878. }
  70879. if (isset($sort['indent'])) {
  70880. $indent = $sort['indent'];
  70881. }
  70882. if (isset($sort['linebreak'])) {
  70883. $multiline = $sort['linebreak'];
  70884. }
  70885. if (isset($sort['entities'])) {
  70886. $entities = $sort['entities'];
  70887. }
  70888. if (isset($sort['sort'])) {
  70889. $sort = $sort['sort'];
  70890. } else {
  70891. $sort = true;
  70892. }
  70893. }
  70894. $string = '';
  70895. if (is_array($attributes) && !empty($attributes)) {
  70896. if ($sort) {
  70897. ksort($attributes);
  70898. }
  70899. if (!$multiline || count($attributes) == 1) {
  70900. foreach ($attributes as $key => $value) {
  70901. if ($entities != XML_UTIL_ENTITIES_NONE) {
  70902. if ($entities === XML_UTIL_CDATA_SECTION) {
  70903. $entities = XML_UTIL_ENTITIES_XML;
  70904. }
  70905. $value = XML_Util::replaceEntities($value, $entities);
  70906. }
  70907. $string .= ' ' . $key . '="' . $value . '"';
  70908. }
  70909. } else {
  70910. $first = true;
  70911. foreach ($attributes as $key => $value) {
  70912. if ($entities != XML_UTIL_ENTITIES_NONE) {
  70913. $value = XML_Util::replaceEntities($value, $entities);
  70914. }
  70915. if ($first) {
  70916. $string .= ' ' . $key . '="' . $value . '"';
  70917. $first = false;
  70918. } else {
  70919. $string .= $linebreak . $indent . $key . '="' . $value . '"';
  70920. }
  70921. }
  70922. }
  70923. }
  70924. return $string;
  70925. }
  70926. /**
  70927. * Collapses empty tags.
  70928. *
  70929. * @param string $xml XML
  70930. * @param int $mode Whether to collapse all empty tags (XML_UTIL_COLLAPSE_ALL)
  70931. * or only XHTML (XML_UTIL_COLLAPSE_XHTML_ONLY) ones.
  70932. *
  70933. * @return string XML
  70934. */
  70935. public static function collapseEmptyTags($xml, $mode = XML_UTIL_COLLAPSE_ALL)
  70936. {
  70937. if (preg_match('~<([^>])+/>~s', $xml, $matches)) {
  70938. // it's already an empty tag
  70939. return $xml;
  70940. }
  70941. switch ($mode) {
  70942. case XML_UTIL_COLLAPSE_ALL:
  70943. $preg1 =
  70944. '~<' .
  70945. '(?:' .
  70946. '(https?://[^:\s]+:\w+)' . // <http://foo.com:bar ($1)
  70947. '|(\w+:\w+)' . // <foo:bar ($2)
  70948. '|(\w+)' . // <foo ($3)
  70949. ')+' .
  70950. '([^>]*)' . // attributes ($4)
  70951. '>' .
  70952. '<\/(\1|\2|\3)>' . // 1, 2, or 3 again ($5)
  70953. '~s'
  70954. ;
  70955. $preg2 =
  70956. '<' .
  70957. '${1}${2}${3}' . // tag (only one should have been populated)
  70958. '${4}' . // attributes
  70959. ' />'
  70960. ;
  70961. return (preg_replace($preg1, $preg2, $xml)?:$xml);
  70962. break;
  70963. case XML_UTIL_COLLAPSE_XHTML_ONLY:
  70964. return (
  70965. preg_replace(
  70966. '/<(area|base(?:font)?|br|col|frame|hr|img|input|isindex|link|meta|'
  70967. . 'param)([^>]*)><\/\\1>/s',
  70968. '<\\1\\2 />',
  70969. $xml
  70970. ) ?: $xml
  70971. );
  70972. break;
  70973. case XML_UTIL_COLLAPSE_NONE:
  70974. // fall thru
  70975. default:
  70976. return $xml;
  70977. }
  70978. }
  70979. /**
  70980. * Create a tag
  70981. *
  70982. * This method will call XML_Util::createTagFromArray(), which
  70983. * is more flexible.
  70984. *
  70985. * <code>
  70986. * require_once 'XML/Util.php';
  70987. *
  70988. * // create an XML tag:
  70989. * $tag = XML_Util::createTag('myNs:myTag',
  70990. * array('foo' => 'bar'),
  70991. * 'This is inside the tag',
  70992. * 'http://www.w3c.org/myNs#');
  70993. * </code>
  70994. *
  70995. * @param string $qname qualified tagname (including namespace)
  70996. * @param array $attributes array containg attributes
  70997. * @param mixed $content the content
  70998. * @param string $namespaceUri URI of the namespace
  70999. * @param int $replaceEntities whether to replace XML special chars in
  71000. * content, embedd it in a CData section
  71001. * or none of both
  71002. * @param bool $multiline whether to create a multiline tag where
  71003. * each attribute gets written to a single line
  71004. * @param string $indent string used to indent attributes
  71005. * (_auto indents attributes so they start
  71006. * at the same column)
  71007. * @param string $linebreak string used for linebreaks
  71008. * @param bool $sortAttributes Whether to sort the attributes or not
  71009. * @param int $collapseTagMode How to handle a content-less, and thus collapseable, tag
  71010. *
  71011. * @return string XML tag
  71012. * @see createTagFromArray()
  71013. * @uses createTagFromArray() to create the tag
  71014. */
  71015. public static function createTag(
  71016. $qname, $attributes = array(), $content = null,
  71017. $namespaceUri = null, $replaceEntities = XML_UTIL_REPLACE_ENTITIES,
  71018. $multiline = false, $indent = '_auto', $linebreak = "\n",
  71019. $sortAttributes = true, $collapseTagMode = XML_UTIL_COLLAPSE_ALL
  71020. ) {
  71021. $tag = array(
  71022. 'qname' => $qname,
  71023. 'attributes' => $attributes
  71024. );
  71025. // add tag content
  71026. if ($content !== null) {
  71027. $tag['content'] = $content;
  71028. }
  71029. // add namespace Uri
  71030. if ($namespaceUri !== null) {
  71031. $tag['namespaceUri'] = $namespaceUri;
  71032. }
  71033. return XML_Util::createTagFromArray(
  71034. $tag, $replaceEntities, $multiline,
  71035. $indent, $linebreak, $sortAttributes,
  71036. $collapseTagMode
  71037. );
  71038. }
  71039. /**
  71040. * Create a tag from an array.
  71041. * This method awaits an array in the following format
  71042. * <pre>
  71043. * array(
  71044. * // qualified name of the tag
  71045. * 'qname' => $qname
  71046. *
  71047. * // namespace prefix (optional, if qname is specified or no namespace)
  71048. * 'namespace' => $namespace
  71049. *
  71050. * // local part of the tagname (optional, if qname is specified)
  71051. * 'localpart' => $localpart,
  71052. *
  71053. * // array containing all attributes (optional)
  71054. * 'attributes' => array(),
  71055. *
  71056. * // tag content (optional)
  71057. * 'content' => $content,
  71058. *
  71059. * // namespaceUri for the given namespace (optional)
  71060. * 'namespaceUri' => $namespaceUri
  71061. * )
  71062. * </pre>
  71063. *
  71064. * <code>
  71065. * require_once 'XML/Util.php';
  71066. *
  71067. * $tag = array(
  71068. * 'qname' => 'foo:bar',
  71069. * 'namespaceUri' => 'http://foo.com',
  71070. * 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'),
  71071. * 'content' => 'I\'m inside the tag',
  71072. * );
  71073. * // creating a tag with qualified name and namespaceUri
  71074. * $string = XML_Util::createTagFromArray($tag);
  71075. * </code>
  71076. *
  71077. * @param array $tag tag definition
  71078. * @param int $replaceEntities whether to replace XML special chars in
  71079. * content, embedd it in a CData section
  71080. * or none of both
  71081. * @param bool $multiline whether to create a multiline tag where each
  71082. * attribute gets written to a single line
  71083. * @param string $indent string used to indent attributes
  71084. * (_auto indents attributes so they start
  71085. * at the same column)
  71086. * @param string $linebreak string used for linebreaks
  71087. * @param bool $sortAttributes Whether to sort the attributes or not
  71088. * @param int $collapseTagMode How to handle a content-less, and thus collapseable, tag
  71089. *
  71090. * @return string XML tag
  71091. *
  71092. * @see createTag()
  71093. * @uses attributesToString() to serialize the attributes of the tag
  71094. * @uses splitQualifiedName() to get local part and namespace of a qualified name
  71095. * @uses createCDataSection()
  71096. * @uses collapseEmptyTags()
  71097. * @uses raiseError()
  71098. */
  71099. public static function createTagFromArray(
  71100. $tag, $replaceEntities = XML_UTIL_REPLACE_ENTITIES,
  71101. $multiline = false, $indent = '_auto', $linebreak = "\n",
  71102. $sortAttributes = true, $collapseTagMode = XML_UTIL_COLLAPSE_ALL
  71103. ) {
  71104. if (isset($tag['content']) && !is_scalar($tag['content'])) {
  71105. return XML_Util::raiseError(
  71106. 'Supplied non-scalar value as tag content',
  71107. XML_UTIL_ERROR_NON_SCALAR_CONTENT
  71108. );
  71109. }
  71110. if (!isset($tag['qname']) && !isset($tag['localPart'])) {
  71111. return XML_Util::raiseError(
  71112. 'You must either supply a qualified name '
  71113. . '(qname) or local tag name (localPart).',
  71114. XML_UTIL_ERROR_NO_TAG_NAME
  71115. );
  71116. }
  71117. // if no attributes hav been set, use empty attributes
  71118. if (!isset($tag['attributes']) || !is_array($tag['attributes'])) {
  71119. $tag['attributes'] = array();
  71120. }
  71121. if (isset($tag['namespaces'])) {
  71122. foreach ($tag['namespaces'] as $ns => $uri) {
  71123. $tag['attributes']['xmlns:' . $ns] = $uri;
  71124. }
  71125. }
  71126. if (!isset($tag['qname'])) {
  71127. // qualified name is not given
  71128. // check for namespace
  71129. if (isset($tag['namespace']) && !empty($tag['namespace'])) {
  71130. $tag['qname'] = $tag['namespace'] . ':' . $tag['localPart'];
  71131. } else {
  71132. $tag['qname'] = $tag['localPart'];
  71133. }
  71134. } elseif (isset($tag['namespaceUri']) && !isset($tag['namespace'])) {
  71135. // namespace URI is set, but no namespace
  71136. $parts = XML_Util::splitQualifiedName($tag['qname']);
  71137. $tag['localPart'] = $parts['localPart'];
  71138. if (isset($parts['namespace'])) {
  71139. $tag['namespace'] = $parts['namespace'];
  71140. }
  71141. }
  71142. if (isset($tag['namespaceUri']) && !empty($tag['namespaceUri'])) {
  71143. // is a namespace given
  71144. if (isset($tag['namespace']) && !empty($tag['namespace'])) {
  71145. $tag['attributes']['xmlns:' . $tag['namespace']]
  71146. = $tag['namespaceUri'];
  71147. } else {
  71148. // define this Uri as the default namespace
  71149. $tag['attributes']['xmlns'] = $tag['namespaceUri'];
  71150. }
  71151. }
  71152. if (!array_key_exists('content', $tag)) {
  71153. $tag['content'] = '';
  71154. }
  71155. // check for multiline attributes
  71156. if ($multiline === true) {
  71157. if ($indent === '_auto') {
  71158. $indent = str_repeat(' ', (strlen($tag['qname'])+2));
  71159. }
  71160. }
  71161. // create attribute list
  71162. $attList = XML_Util::attributesToString(
  71163. $tag['attributes'],
  71164. $sortAttributes, $multiline, $indent, $linebreak
  71165. );
  71166. switch ($replaceEntities) {
  71167. case XML_UTIL_ENTITIES_NONE:
  71168. break;
  71169. case XML_UTIL_CDATA_SECTION:
  71170. $tag['content'] = XML_Util::createCDataSection($tag['content']);
  71171. break;
  71172. default:
  71173. $tag['content'] = XML_Util::replaceEntities(
  71174. $tag['content'], $replaceEntities
  71175. );
  71176. break;
  71177. }
  71178. $tag = sprintf(
  71179. '<%s%s>%s</%s>', $tag['qname'], $attList, $tag['content'],
  71180. $tag['qname']
  71181. );
  71182. return self::collapseEmptyTags($tag, $collapseTagMode);
  71183. }
  71184. /**
  71185. * Create a start element
  71186. *
  71187. * <code>
  71188. * require_once 'XML/Util.php';
  71189. *
  71190. * // create an XML start element:
  71191. * $tag = XML_Util::createStartElement('myNs:myTag',
  71192. * array('foo' => 'bar') ,'http://www.w3c.org/myNs#');
  71193. * </code>
  71194. *
  71195. * @param string $qname qualified tagname (including namespace)
  71196. * @param array $attributes array containg attributes
  71197. * @param string $namespaceUri URI of the namespace
  71198. * @param bool $multiline whether to create a multiline tag where each
  71199. * attribute gets written to a single line
  71200. * @param string $indent string used to indent attributes (_auto indents
  71201. * attributes so they start at the same column)
  71202. * @param string $linebreak string used for linebreaks
  71203. * @param bool $sortAttributes Whether to sort the attributes or not
  71204. *
  71205. * @return string XML start element
  71206. * @see createEndElement(), createTag()
  71207. */
  71208. public static function createStartElement(
  71209. $qname, $attributes = array(), $namespaceUri = null,
  71210. $multiline = false, $indent = '_auto', $linebreak = "\n",
  71211. $sortAttributes = true
  71212. ) {
  71213. // if no attributes hav been set, use empty attributes
  71214. if (!isset($attributes) || !is_array($attributes)) {
  71215. $attributes = array();
  71216. }
  71217. if ($namespaceUri != null) {
  71218. $parts = XML_Util::splitQualifiedName($qname);
  71219. }
  71220. // check for multiline attributes
  71221. if ($multiline === true) {
  71222. if ($indent === '_auto') {
  71223. $indent = str_repeat(' ', (strlen($qname)+2));
  71224. }
  71225. }
  71226. if ($namespaceUri != null) {
  71227. // is a namespace given
  71228. if (isset($parts['namespace']) && !empty($parts['namespace'])) {
  71229. $attributes['xmlns:' . $parts['namespace']] = $namespaceUri;
  71230. } else {
  71231. // define this Uri as the default namespace
  71232. $attributes['xmlns'] = $namespaceUri;
  71233. }
  71234. }
  71235. // create attribute list
  71236. $attList = XML_Util::attributesToString(
  71237. $attributes, $sortAttributes,
  71238. $multiline, $indent, $linebreak
  71239. );
  71240. $element = sprintf('<%s%s>', $qname, $attList);
  71241. return $element;
  71242. }
  71243. /**
  71244. * Create an end element
  71245. *
  71246. * <code>
  71247. * require_once 'XML/Util.php';
  71248. *
  71249. * // create an XML start element:
  71250. * $tag = XML_Util::createEndElement('myNs:myTag');
  71251. * </code>
  71252. *
  71253. * @param string $qname qualified tagname (including namespace)
  71254. *
  71255. * @return string XML end element
  71256. * @see createStartElement(), createTag()
  71257. */
  71258. public static function createEndElement($qname)
  71259. {
  71260. $element = sprintf('</%s>', $qname);
  71261. return $element;
  71262. }
  71263. /**
  71264. * Create an XML comment
  71265. *
  71266. * <code>
  71267. * require_once 'XML/Util.php';
  71268. *
  71269. * // create an XML start element:
  71270. * $tag = XML_Util::createComment('I am a comment');
  71271. * </code>
  71272. *
  71273. * @param string $content content of the comment
  71274. *
  71275. * @return string XML comment
  71276. */
  71277. public static function createComment($content)
  71278. {
  71279. $comment = sprintf('<!-- %s -->', $content);
  71280. return $comment;
  71281. }
  71282. /**
  71283. * Create a CData section
  71284. *
  71285. * <code>
  71286. * require_once 'XML/Util.php';
  71287. *
  71288. * // create a CData section
  71289. * $tag = XML_Util::createCDataSection('I am content.');
  71290. * </code>
  71291. *
  71292. * @param string $data data of the CData section
  71293. *
  71294. * @return string CData section with content
  71295. */
  71296. public static function createCDataSection($data)
  71297. {
  71298. return sprintf(
  71299. '<![CDATA[%s]]>',
  71300. preg_replace('/\]\]>/', ']]]]><![CDATA[>', strval($data))
  71301. );
  71302. }
  71303. /**
  71304. * Split qualified name and return namespace and local part
  71305. *
  71306. * <code>
  71307. * require_once 'XML/Util.php';
  71308. *
  71309. * // split qualified tag
  71310. * $parts = XML_Util::splitQualifiedName('xslt:stylesheet');
  71311. * </code>
  71312. * the returned array will contain two elements:
  71313. * <pre>
  71314. * array(
  71315. * 'namespace' => 'xslt',
  71316. * 'localPart' => 'stylesheet'
  71317. * );
  71318. * </pre>
  71319. *
  71320. * @param string $qname qualified tag name
  71321. * @param string $defaultNs default namespace (optional)
  71322. *
  71323. * @return array array containing namespace and local part
  71324. */
  71325. public static function splitQualifiedName($qname, $defaultNs = null)
  71326. {
  71327. if (strstr($qname, ':')) {
  71328. $tmp = explode(':', $qname);
  71329. return array(
  71330. 'namespace' => $tmp[0],
  71331. 'localPart' => $tmp[1]
  71332. );
  71333. }
  71334. return array(
  71335. 'namespace' => $defaultNs,
  71336. 'localPart' => $qname
  71337. );
  71338. }
  71339. /**
  71340. * Check, whether string is valid XML name
  71341. *
  71342. * <p>XML names are used for tagname, attribute names and various
  71343. * other, lesser known entities.</p>
  71344. * <p>An XML name may only consist of alphanumeric characters,
  71345. * dashes, undescores and periods, and has to start with a letter
  71346. * or an underscore.</p>
  71347. *
  71348. * <code>
  71349. * require_once 'XML/Util.php';
  71350. *
  71351. * // verify tag name
  71352. * $result = XML_Util::isValidName('invalidTag?');
  71353. * if (is_a($result, 'PEAR_Error')) {
  71354. * print 'Invalid XML name: ' . $result->getMessage();
  71355. * }
  71356. * </code>
  71357. *
  71358. * @param string $string string that should be checked
  71359. *
  71360. * @return mixed true, if string is a valid XML name, PEAR error otherwise
  71361. *
  71362. * @todo support for other charsets
  71363. * @todo PEAR CS - unable to avoid 85-char limit on second preg_match
  71364. */
  71365. public static function isValidName($string)
  71366. {
  71367. // check for invalid chars
  71368. if (!is_string($string) || !strlen($string) || !preg_match('/^[[:alpha:]_]\\z/', $string[0])) {
  71369. return XML_Util::raiseError(
  71370. 'XML names may only start with letter or underscore',
  71371. XML_UTIL_ERROR_INVALID_START
  71372. );
  71373. }
  71374. // check for invalid chars
  71375. $match = preg_match(
  71376. '/^([[:alpha:]_]([[:alnum:]\-\.]*)?:)?'
  71377. . '[[:alpha:]_]([[:alnum:]\_\-\.]+)?\\z/',
  71378. $string
  71379. );
  71380. if (!$match) {
  71381. return XML_Util::raiseError(
  71382. 'XML names may only contain alphanumeric '
  71383. . 'chars, period, hyphen, colon and underscores',
  71384. XML_UTIL_ERROR_INVALID_CHARS
  71385. );
  71386. }
  71387. // XML name is valid
  71388. return true;
  71389. }
  71390. /**
  71391. * Replacement for XML_Util::raiseError
  71392. *
  71393. * Avoids the necessity to always require
  71394. * PEAR.php
  71395. *
  71396. * @param string $msg error message
  71397. * @param int $code error code
  71398. *
  71399. * @return PEAR_Error
  71400. * @todo PEAR CS - should this use include_once instead?
  71401. */
  71402. public static function raiseError($msg, $code)
  71403. {
  71404. include_once 'PEAR.php';
  71405. return PEAR::raiseError($msg, $code);
  71406. }
  71407. }
  71408. ?>
  71409. ��������������������������������������������������������������������������������package.sig�����������������������������������������������������������������������������������������0000644�0001750�0001750�00000000303�13647063055�013032 �0����������������������������������������������������������������������������������������������������ustar �ashnazg�������������������������ashnazg����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������-----BEGIN PGP SIGNATURE-----
  71410. iF0EABECAB0WIQQQ9oz3P4qkJvYXdSlyoyG6wkXxdQUCXpxmLQAKCRByoyG6wkXx
  71411. dcz7AJwJUmyZewMqdBXXR/Nd1+ivgZbaBwCgrBxf/DX0gXpIz5xpXKWhutC2dW0=
  71412. =IfPG
  71413. -----END PGP SIGNATURE-----
  71414. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?php
  71415. /**
  71416. * PEAR_Installer
  71417. *
  71418. * PHP versions 4 and 5
  71419. *
  71420. * @category pear
  71421. * @package PEAR
  71422. * @author Stig Bakken <ssb@php.net>
  71423. * @author Tomas V.V. Cox <cox@idecnet.com>
  71424. * @author Martin Jansen <mj@php.net>
  71425. * @author Greg Beaver <cellog@php.net>
  71426. * @copyright 1997-2009 The Authors
  71427. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  71428. * @link http://pear.php.net/package/PEAR
  71429. * @since File available since Release 0.1
  71430. */
  71431. /**
  71432. * Used for installation groups in package.xml 2.0 and platform exceptions
  71433. */
  71434. require_once 'phar://go-pear.phar/' . 'OS/Guess.php';
  71435. require_once 'phar://go-pear.phar/' . 'PEAR/Downloader.php';
  71436. define('PEAR_INSTALLER_NOBINARY', -240);
  71437. /**
  71438. * Administration class used to install PEAR packages and maintain the
  71439. * installed package database.
  71440. *
  71441. * @category pear
  71442. * @package PEAR
  71443. * @author Stig Bakken <ssb@php.net>
  71444. * @author Tomas V.V. Cox <cox@idecnet.com>
  71445. * @author Martin Jansen <mj@php.net>
  71446. * @author Greg Beaver <cellog@php.net>
  71447. * @copyright 1997-2009 The Authors
  71448. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  71449. * @version Release: 1.10.16
  71450. * @link http://pear.php.net/package/PEAR
  71451. * @since Class available since Release 0.1
  71452. */
  71453. class PEAR_Installer extends PEAR_Downloader
  71454. {
  71455. // {{{ properties
  71456. /** name of the package directory, for example Foo-1.0
  71457. * @var string
  71458. */
  71459. var $pkgdir;
  71460. /** directory where PHP code files go
  71461. * @var string
  71462. */
  71463. var $phpdir;
  71464. /** directory where PHP extension files go
  71465. * @var string
  71466. */
  71467. var $extdir;
  71468. /** directory where documentation goes
  71469. * @var string
  71470. */
  71471. var $docdir;
  71472. /** installation root directory (ala PHP's INSTALL_ROOT or
  71473. * automake's DESTDIR
  71474. * @var string
  71475. */
  71476. var $installroot = '';
  71477. /** debug level
  71478. * @var int
  71479. */
  71480. var $debug = 1;
  71481. /** temporary directory
  71482. * @var string
  71483. */
  71484. var $tmpdir;
  71485. /**
  71486. * PEAR_Registry object used by the installer
  71487. * @var PEAR_Registry
  71488. */
  71489. var $registry;
  71490. /**
  71491. * array of PEAR_Downloader_Packages
  71492. * @var array
  71493. */
  71494. var $_downloadedPackages;
  71495. /** List of file transactions queued for an install/upgrade/uninstall.
  71496. *
  71497. * Format:
  71498. * array(
  71499. * 0 => array("rename => array("from-file", "to-file")),
  71500. * 1 => array("delete" => array("file-to-delete")),
  71501. * ...
  71502. * )
  71503. *
  71504. * @var array
  71505. */
  71506. var $file_operations = array();
  71507. // }}}
  71508. // {{{ constructor
  71509. /**
  71510. * PEAR_Installer constructor.
  71511. *
  71512. * @param object $ui user interface object (instance of PEAR_Frontend_*)
  71513. *
  71514. * @access public
  71515. */
  71516. function __construct(&$ui)
  71517. {
  71518. parent::__construct($ui, array(), null);
  71519. $this->setFrontendObject($ui);
  71520. $this->debug = $this->config->get('verbose');
  71521. }
  71522. function setOptions($options)
  71523. {
  71524. $this->_options = $options;
  71525. }
  71526. function setConfig(&$config)
  71527. {
  71528. $this->config = &$config;
  71529. $this->_registry = &$config->getRegistry();
  71530. }
  71531. // }}}
  71532. function _removeBackups($files)
  71533. {
  71534. foreach ($files as $path) {
  71535. $this->addFileOperation('removebackup', array($path));
  71536. }
  71537. }
  71538. // {{{ _deletePackageFiles()
  71539. /**
  71540. * Delete a package's installed files, does not remove empty directories.
  71541. *
  71542. * @param string package name
  71543. * @param string channel name
  71544. * @param bool if true, then files are backed up first
  71545. * @return bool TRUE on success, or a PEAR error on failure
  71546. * @access protected
  71547. */
  71548. function _deletePackageFiles($package, $channel = false, $backup = false)
  71549. {
  71550. if (!$channel) {
  71551. $channel = 'pear.php.net';
  71552. }
  71553. if (!strlen($package)) {
  71554. return $this->raiseError("No package to uninstall given");
  71555. }
  71556. if (strtolower($package) == 'pear' && $channel == 'pear.php.net') {
  71557. // to avoid race conditions, include all possible needed files
  71558. require_once 'phar://go-pear.phar/' . 'PEAR/Task/Common.php';
  71559. require_once 'phar://go-pear.phar/' . 'PEAR/Task/Replace.php';
  71560. require_once 'phar://go-pear.phar/' . 'PEAR/Task/Unixeol.php';
  71561. require_once 'phar://go-pear.phar/' . 'PEAR/Task/Windowseol.php';
  71562. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/v1.php';
  71563. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/v2.php';
  71564. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/Generator/v1.php';
  71565. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/Generator/v2.php';
  71566. }
  71567. $filelist = $this->_registry->packageInfo($package, 'filelist', $channel);
  71568. if ($filelist == null) {
  71569. return $this->raiseError("$channel/$package not installed");
  71570. }
  71571. $ret = array();
  71572. foreach ($filelist as $file => $props) {
  71573. if (empty($props['installed_as'])) {
  71574. continue;
  71575. }
  71576. $path = $props['installed_as'];
  71577. if ($backup) {
  71578. $this->addFileOperation('backup', array($path));
  71579. $ret[] = $path;
  71580. }
  71581. $this->addFileOperation('delete', array($path));
  71582. }
  71583. if ($backup) {
  71584. return $ret;
  71585. }
  71586. return true;
  71587. }
  71588. // }}}
  71589. // {{{ _installFile()
  71590. /**
  71591. * @param string filename
  71592. * @param array attributes from <file> tag in package.xml
  71593. * @param string path to install the file in
  71594. * @param array options from command-line
  71595. * @access private
  71596. */
  71597. function _installFile($file, $atts, $tmp_path, $options)
  71598. {
  71599. // {{{ return if this file is meant for another platform
  71600. static $os;
  71601. if (!isset($this->_registry)) {
  71602. $this->_registry = &$this->config->getRegistry();
  71603. }
  71604. if (isset($atts['platform'])) {
  71605. if (empty($os)) {
  71606. $os = new OS_Guess();
  71607. }
  71608. if (strlen($atts['platform']) && $atts['platform'][0] == '!') {
  71609. $negate = true;
  71610. $platform = substr($atts['platform'], 1);
  71611. } else {
  71612. $negate = false;
  71613. $platform = $atts['platform'];
  71614. }
  71615. if ((bool) $os->matchSignature($platform) === $negate) {
  71616. $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")");
  71617. return PEAR_INSTALLER_SKIPPED;
  71618. }
  71619. }
  71620. // }}}
  71621. $channel = $this->pkginfo->getChannel();
  71622. // {{{ assemble the destination paths
  71623. switch ($atts['role']) {
  71624. case 'src':
  71625. case 'extsrc':
  71626. $this->source_files++;
  71627. return;
  71628. case 'doc':
  71629. case 'data':
  71630. case 'test':
  71631. $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel) .
  71632. DIRECTORY_SEPARATOR . $this->pkginfo->getPackage();
  71633. unset($atts['baseinstalldir']);
  71634. break;
  71635. case 'ext':
  71636. case 'php':
  71637. $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel);
  71638. break;
  71639. case 'script':
  71640. $dest_dir = $this->config->get('bin_dir', null, $channel);
  71641. break;
  71642. default:
  71643. return $this->raiseError("Invalid role `$atts[role]' for file $file");
  71644. }
  71645. $save_destdir = $dest_dir;
  71646. if (!empty($atts['baseinstalldir'])) {
  71647. $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
  71648. }
  71649. if (dirname($file) != '.' && empty($atts['install-as'])) {
  71650. $dest_dir .= DIRECTORY_SEPARATOR . dirname($file);
  71651. }
  71652. if (empty($atts['install-as'])) {
  71653. $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file);
  71654. } else {
  71655. $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as'];
  71656. }
  71657. $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file;
  71658. // Clean up the DIRECTORY_SEPARATOR mess
  71659. $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
  71660. list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
  71661. array(DIRECTORY_SEPARATOR,
  71662. DIRECTORY_SEPARATOR,
  71663. DIRECTORY_SEPARATOR),
  71664. array($dest_file, $orig_file));
  71665. $final_dest_file = $installed_as = $dest_file;
  71666. if (isset($this->_options['packagingroot'])) {
  71667. $installedas_dest_dir = dirname($final_dest_file);
  71668. $installedas_dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
  71669. $final_dest_file = $this->_prependPath($final_dest_file, $this->_options['packagingroot']);
  71670. } else {
  71671. $installedas_dest_dir = dirname($final_dest_file);
  71672. $installedas_dest_file = $installedas_dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
  71673. }
  71674. $dest_dir = dirname($final_dest_file);
  71675. $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
  71676. if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
  71677. return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
  71678. }
  71679. // }}}
  71680. if (empty($this->_options['register-only']) &&
  71681. (!file_exists($dest_dir) || !is_dir($dest_dir))) {
  71682. if (!$this->mkDirHier($dest_dir)) {
  71683. return $this->raiseError("failed to mkdir $dest_dir",
  71684. PEAR_INSTALLER_FAILED);
  71685. }
  71686. $this->log(3, "+ mkdir $dest_dir");
  71687. }
  71688. // pretty much nothing happens if we are only registering the install
  71689. if (empty($this->_options['register-only'])) {
  71690. if (empty($atts['replacements'])) {
  71691. if (!file_exists($orig_file)) {
  71692. return $this->raiseError("file $orig_file does not exist",
  71693. PEAR_INSTALLER_FAILED);
  71694. }
  71695. if (!@copy($orig_file, $dest_file)) {
  71696. return $this->raiseError(
  71697. "failed to write $dest_file: " . error_get_last()["message"],
  71698. PEAR_INSTALLER_FAILED);
  71699. }
  71700. $this->log(3, "+ cp $orig_file $dest_file");
  71701. if (isset($atts['md5sum'])) {
  71702. $md5sum = md5_file($dest_file);
  71703. }
  71704. } else {
  71705. // {{{ file with replacements
  71706. if (!file_exists($orig_file)) {
  71707. return $this->raiseError("file does not exist",
  71708. PEAR_INSTALLER_FAILED);
  71709. }
  71710. $contents = file_get_contents($orig_file);
  71711. if ($contents === false) {
  71712. $contents = '';
  71713. }
  71714. if (isset($atts['md5sum'])) {
  71715. $md5sum = md5($contents);
  71716. }
  71717. $subst_from = $subst_to = array();
  71718. foreach ($atts['replacements'] as $a) {
  71719. $to = '';
  71720. if ($a['type'] == 'php-const') {
  71721. if (preg_match('/^[a-z0-9_]+\\z/i', $a['to'])) {
  71722. eval("\$to = $a[to];");
  71723. } else {
  71724. if (!isset($options['soft'])) {
  71725. $this->log(0, "invalid php-const replacement: $a[to]");
  71726. }
  71727. continue;
  71728. }
  71729. } elseif ($a['type'] == 'pear-config') {
  71730. if ($a['to'] == 'master_server') {
  71731. $chan = $this->_registry->getChannel($channel);
  71732. if (!PEAR::isError($chan)) {
  71733. $to = $chan->getServer();
  71734. } else {
  71735. $to = $this->config->get($a['to'], null, $channel);
  71736. }
  71737. } else {
  71738. $to = $this->config->get($a['to'], null, $channel);
  71739. }
  71740. if (is_null($to)) {
  71741. if (!isset($options['soft'])) {
  71742. $this->log(0, "invalid pear-config replacement: $a[to]");
  71743. }
  71744. continue;
  71745. }
  71746. } elseif ($a['type'] == 'package-info') {
  71747. if ($t = $this->pkginfo->packageInfo($a['to'])) {
  71748. $to = $t;
  71749. } else {
  71750. if (!isset($options['soft'])) {
  71751. $this->log(0, "invalid package-info replacement: $a[to]");
  71752. }
  71753. continue;
  71754. }
  71755. }
  71756. if (!is_null($to)) {
  71757. $subst_from[] = $a['from'];
  71758. $subst_to[] = $to;
  71759. }
  71760. }
  71761. $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file");
  71762. if (sizeof($subst_from)) {
  71763. $contents = str_replace($subst_from, $subst_to, $contents);
  71764. }
  71765. $wp = @fopen($dest_file, "wb");
  71766. if (!is_resource($wp)) {
  71767. return $this->raiseError(
  71768. "failed to create $dest_file: " . error_get_last()["message"],
  71769. PEAR_INSTALLER_FAILED);
  71770. }
  71771. if (@fwrite($wp, $contents) === false) {
  71772. return $this->raiseError(
  71773. "failed writing to $dest_file: " . error_get_last()["message"],
  71774. PEAR_INSTALLER_FAILED);
  71775. }
  71776. fclose($wp);
  71777. // }}}
  71778. }
  71779. // {{{ check the md5
  71780. if (isset($md5sum)) {
  71781. if (strtolower($md5sum) === strtolower($atts['md5sum'])) {
  71782. $this->log(2, "md5sum ok: $final_dest_file");
  71783. } else {
  71784. if (empty($options['force'])) {
  71785. // delete the file
  71786. if (file_exists($dest_file)) {
  71787. unlink($dest_file);
  71788. }
  71789. if (!isset($options['ignore-errors'])) {
  71790. return $this->raiseError("bad md5sum for file $final_dest_file",
  71791. PEAR_INSTALLER_FAILED);
  71792. }
  71793. if (!isset($options['soft'])) {
  71794. $this->log(0, "warning : bad md5sum for file $final_dest_file");
  71795. }
  71796. } else {
  71797. if (!isset($options['soft'])) {
  71798. $this->log(0, "warning : bad md5sum for file $final_dest_file");
  71799. }
  71800. }
  71801. }
  71802. }
  71803. // }}}
  71804. // {{{ set file permissions
  71805. if (!OS_WINDOWS) {
  71806. if ($atts['role'] == 'script') {
  71807. $mode = 0777 & ~(int)octdec($this->config->get('umask'));
  71808. $this->log(3, "+ chmod +x $dest_file");
  71809. } else {
  71810. $mode = 0666 & ~(int)octdec($this->config->get('umask'));
  71811. }
  71812. if ($atts['role'] != 'src') {
  71813. $this->addFileOperation("chmod", array($mode, $dest_file));
  71814. if (!@chmod($dest_file, $mode)) {
  71815. if (!isset($options['soft'])) {
  71816. $this->log(0, "failed to change mode of $dest_file: " .
  71817. error_get_last()["message"]);
  71818. }
  71819. }
  71820. }
  71821. }
  71822. // }}}
  71823. if ($atts['role'] == 'src') {
  71824. rename($dest_file, $final_dest_file);
  71825. $this->log(2, "renamed source file $dest_file to $final_dest_file");
  71826. } else {
  71827. $this->addFileOperation("rename", array($dest_file, $final_dest_file,
  71828. $atts['role'] == 'ext'));
  71829. }
  71830. }
  71831. // Store the full path where the file was installed for easy unistall
  71832. if ($atts['role'] != 'script') {
  71833. $loc = $this->config->get($atts['role'] . '_dir');
  71834. } else {
  71835. $loc = $this->config->get('bin_dir');
  71836. }
  71837. if ($atts['role'] != 'src') {
  71838. $this->addFileOperation("installed_as", array($file, $installed_as,
  71839. $loc,
  71840. dirname(substr($installedas_dest_file, strlen($loc)))));
  71841. }
  71842. //$this->log(2, "installed: $dest_file");
  71843. return PEAR_INSTALLER_OK;
  71844. }
  71845. // }}}
  71846. // {{{ _installFile2()
  71847. /**
  71848. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  71849. * @param string filename
  71850. * @param array attributes from <file> tag in package.xml
  71851. * @param string path to install the file in
  71852. * @param array options from command-line
  71853. * @access private
  71854. */
  71855. function _installFile2(&$pkg, $file, &$real_atts, $tmp_path, $options)
  71856. {
  71857. $atts = $real_atts;
  71858. if (!isset($this->_registry)) {
  71859. $this->_registry = &$this->config->getRegistry();
  71860. }
  71861. $channel = $pkg->getChannel();
  71862. // {{{ assemble the destination paths
  71863. if (!in_array($atts['attribs']['role'],
  71864. PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
  71865. return $this->raiseError('Invalid role `' . $atts['attribs']['role'] .
  71866. "' for file $file");
  71867. }
  71868. $role = &PEAR_Installer_Role::factory($pkg, $atts['attribs']['role'], $this->config);
  71869. $err = $role->setup($this, $pkg, $atts['attribs'], $file);
  71870. if (PEAR::isError($err)) {
  71871. return $err;
  71872. }
  71873. if (!$role->isInstallable()) {
  71874. return;
  71875. }
  71876. $info = $role->processInstallation($pkg, $atts['attribs'], $file, $tmp_path);
  71877. if (PEAR::isError($info)) {
  71878. return $info;
  71879. }
  71880. list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info;
  71881. if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
  71882. return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
  71883. }
  71884. $final_dest_file = $installed_as = $dest_file;
  71885. if (isset($this->_options['packagingroot'])) {
  71886. $final_dest_file = $this->_prependPath($final_dest_file,
  71887. $this->_options['packagingroot']);
  71888. }
  71889. $dest_dir = dirname($final_dest_file);
  71890. $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
  71891. // }}}
  71892. if (empty($this->_options['register-only'])) {
  71893. if (!file_exists($dest_dir) || !is_dir($dest_dir)) {
  71894. if (!$this->mkDirHier($dest_dir)) {
  71895. return $this->raiseError("failed to mkdir $dest_dir",
  71896. PEAR_INSTALLER_FAILED);
  71897. }
  71898. $this->log(3, "+ mkdir $dest_dir");
  71899. }
  71900. }
  71901. $attribs = $atts['attribs'];
  71902. unset($atts['attribs']);
  71903. // pretty much nothing happens if we are only registering the install
  71904. if (empty($this->_options['register-only'])) {
  71905. if (!count($atts)) { // no tasks
  71906. if (!file_exists($orig_file)) {
  71907. return $this->raiseError("file $orig_file does not exist",
  71908. PEAR_INSTALLER_FAILED);
  71909. }
  71910. if (!@copy($orig_file, $dest_file)) {
  71911. return $this->raiseError(
  71912. "failed to write $dest_file: " . error_get_last()["message"],
  71913. PEAR_INSTALLER_FAILED);
  71914. }
  71915. $this->log(3, "+ cp $orig_file $dest_file");
  71916. if (isset($attribs['md5sum'])) {
  71917. $md5sum = md5_file($dest_file);
  71918. }
  71919. } else { // file with tasks
  71920. if (!file_exists($orig_file)) {
  71921. return $this->raiseError("file $orig_file does not exist",
  71922. PEAR_INSTALLER_FAILED);
  71923. }
  71924. $contents = file_get_contents($orig_file);
  71925. if ($contents === false) {
  71926. $contents = '';
  71927. }
  71928. if (isset($attribs['md5sum'])) {
  71929. $md5sum = md5($contents);
  71930. }
  71931. foreach ($atts as $tag => $raw) {
  71932. $tag = str_replace(array($pkg->getTasksNs() . ':', '-'), array('', '_'), $tag);
  71933. $task = "PEAR_Task_$tag";
  71934. $task = new $task($this->config, $this, PEAR_TASK_INSTALL);
  71935. if (!$task->isScript()) { // scripts are only handled after installation
  71936. $task->init($raw, $attribs, $pkg->getLastInstalledVersion());
  71937. $res = $task->startSession($pkg, $contents, $final_dest_file);
  71938. if ($res === false) {
  71939. continue; // skip this file
  71940. }
  71941. if (PEAR::isError($res)) {
  71942. return $res;
  71943. }
  71944. $contents = $res; // save changes
  71945. }
  71946. $wp = @fopen($dest_file, "wb");
  71947. if (!is_resource($wp)) {
  71948. return $this->raiseError(
  71949. "failed to create $dest_file: " . error_get_last()["message"],
  71950. PEAR_INSTALLER_FAILED);
  71951. }
  71952. if (fwrite($wp, $contents) === false) {
  71953. return $this->raiseError(
  71954. "failed writing to $dest_file: " . error_get_last()["message"],
  71955. PEAR_INSTALLER_FAILED);
  71956. }
  71957. fclose($wp);
  71958. }
  71959. }
  71960. // {{{ check the md5
  71961. if (isset($md5sum)) {
  71962. // Make sure the original md5 sum matches with expected
  71963. if (strtolower($md5sum) === strtolower($attribs['md5sum'])) {
  71964. $this->log(2, "md5sum ok: $final_dest_file");
  71965. if (isset($contents)) {
  71966. // set md5 sum based on $content in case any tasks were run.
  71967. $real_atts['attribs']['md5sum'] = md5($contents);
  71968. }
  71969. } else {
  71970. if (empty($options['force'])) {
  71971. // delete the file
  71972. if (file_exists($dest_file)) {
  71973. unlink($dest_file);
  71974. }
  71975. if (!isset($options['ignore-errors'])) {
  71976. return $this->raiseError("bad md5sum for file $final_dest_file",
  71977. PEAR_INSTALLER_FAILED);
  71978. }
  71979. if (!isset($options['soft'])) {
  71980. $this->log(0, "warning : bad md5sum for file $final_dest_file");
  71981. }
  71982. } else {
  71983. if (!isset($options['soft'])) {
  71984. $this->log(0, "warning : bad md5sum for file $final_dest_file");
  71985. }
  71986. }
  71987. }
  71988. } else {
  71989. $real_atts['attribs']['md5sum'] = md5_file($dest_file);
  71990. }
  71991. // }}}
  71992. // {{{ set file permissions
  71993. if (!OS_WINDOWS) {
  71994. if ($role->isExecutable()) {
  71995. $mode = 0777 & ~(int)octdec($this->config->get('umask'));
  71996. $this->log(3, "+ chmod +x $dest_file");
  71997. } else {
  71998. $mode = 0666 & ~(int)octdec($this->config->get('umask'));
  71999. }
  72000. if ($attribs['role'] != 'src') {
  72001. $this->addFileOperation("chmod", array($mode, $dest_file));
  72002. if (!@chmod($dest_file, $mode)) {
  72003. if (!isset($options['soft'])) {
  72004. $this->log(0, "failed to change mode of $dest_file: " .
  72005. error_get_last()["message"]);
  72006. }
  72007. }
  72008. }
  72009. }
  72010. // }}}
  72011. if ($attribs['role'] == 'src') {
  72012. rename($dest_file, $final_dest_file);
  72013. $this->log(2, "renamed source file $dest_file to $final_dest_file");
  72014. } else {
  72015. $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension()));
  72016. }
  72017. }
  72018. // Store the full path where the file was installed for easy uninstall
  72019. if ($attribs['role'] != 'src') {
  72020. $loc = $this->config->get($role->getLocationConfig(), null, $channel);
  72021. $this->addFileOperation('installed_as', array($file, $installed_as,
  72022. $loc,
  72023. dirname(substr($installed_as, strlen($loc)))));
  72024. }
  72025. //$this->log(2, "installed: $dest_file");
  72026. return PEAR_INSTALLER_OK;
  72027. }
  72028. // }}}
  72029. // {{{ addFileOperation()
  72030. /**
  72031. * Add a file operation to the current file transaction.
  72032. *
  72033. * @see startFileTransaction()
  72034. * @param string $type This can be one of:
  72035. * - rename: rename a file ($data has 3 values)
  72036. * - backup: backup an existing file ($data has 1 value)
  72037. * - removebackup: clean up backups created during install ($data has 1 value)
  72038. * - chmod: change permissions on a file ($data has 2 values)
  72039. * - delete: delete a file ($data has 1 value)
  72040. * - rmdir: delete a directory if empty ($data has 1 value)
  72041. * - installed_as: mark a file as installed ($data has 4 values).
  72042. * @param array $data For all file operations, this array must contain the
  72043. * full path to the file or directory that is being operated on. For
  72044. * the rename command, the first parameter must be the file to rename,
  72045. * the second its new name, the third whether this is a PHP extension.
  72046. *
  72047. * The installed_as operation contains 4 elements in this order:
  72048. * 1. Filename as listed in the filelist element from package.xml
  72049. * 2. Full path to the installed file
  72050. * 3. Full path from the php_dir configuration variable used in this
  72051. * installation
  72052. * 4. Relative path from the php_dir that this file is installed in
  72053. */
  72054. function addFileOperation($type, $data)
  72055. {
  72056. if (!is_array($data)) {
  72057. return $this->raiseError('Internal Error: $data in addFileOperation'
  72058. . ' must be an array, was ' . gettype($data));
  72059. }
  72060. if ($type == 'chmod') {
  72061. $octmode = decoct($data[0]);
  72062. $this->log(3, "adding to transaction: $type $octmode $data[1]");
  72063. } else {
  72064. $this->log(3, "adding to transaction: $type " . implode(" ", $data));
  72065. }
  72066. $this->file_operations[] = array($type, $data);
  72067. }
  72068. // }}}
  72069. // {{{ startFileTransaction()
  72070. function startFileTransaction($rollback_in_case = false)
  72071. {
  72072. if (count($this->file_operations) && $rollback_in_case) {
  72073. $this->rollbackFileTransaction();
  72074. }
  72075. $this->file_operations = array();
  72076. }
  72077. // }}}
  72078. // {{{ commitFileTransaction()
  72079. function commitFileTransaction()
  72080. {
  72081. // {{{ first, check permissions and such manually
  72082. $errors = array();
  72083. foreach ($this->file_operations as $key => $tr) {
  72084. list($type, $data) = $tr;
  72085. switch ($type) {
  72086. case 'rename':
  72087. if (!file_exists($data[0])) {
  72088. $errors[] = "cannot rename file $data[0], doesn't exist";
  72089. }
  72090. // check that dest dir. is writable
  72091. if (!is_writable(dirname($data[1]))) {
  72092. $errors[] = "permission denied ($type): $data[1]";
  72093. }
  72094. break;
  72095. case 'chmod':
  72096. // check that file is writable
  72097. if (!is_writable($data[1])) {
  72098. $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]);
  72099. }
  72100. break;
  72101. case 'delete':
  72102. if (!file_exists($data[0])) {
  72103. $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted");
  72104. }
  72105. // check that directory is writable
  72106. if (file_exists($data[0])) {
  72107. if (!is_writable(dirname($data[0]))) {
  72108. $errors[] = "permission denied ($type): $data[0]";
  72109. } else {
  72110. // make sure the file to be deleted can be opened for writing
  72111. $fp = false;
  72112. if (!is_dir($data[0]) &&
  72113. (!is_writable($data[0]) || !($fp = @fopen($data[0], 'a')))) {
  72114. $errors[] = "permission denied ($type): $data[0]";
  72115. } elseif ($fp) {
  72116. fclose($fp);
  72117. }
  72118. }
  72119. /* Verify we are not deleting a file owned by another package
  72120. * This can happen when a file moves from package A to B in
  72121. * an upgrade ala http://pear.php.net/17986
  72122. */
  72123. $info = array(
  72124. 'package' => strtolower($this->pkginfo->getName()),
  72125. 'channel' => strtolower($this->pkginfo->getChannel()),
  72126. );
  72127. $result = $this->_registry->checkFileMap($data[0], $info, '1.1');
  72128. if (is_array($result)) {
  72129. $res = array_diff($result, $info);
  72130. if (!empty($res)) {
  72131. $new = $this->_registry->getPackage($result[1], $result[0]);
  72132. $this->file_operations[$key] = false;
  72133. $pkginfoName = $this->pkginfo->getName();
  72134. $newChannel = $new->getChannel();
  72135. $newPackage = $new->getName();
  72136. $this->log(3, "file $data[0] was scheduled for removal from $pkginfoName but is owned by $newChannel/$newPackage, removal has been cancelled.");
  72137. }
  72138. }
  72139. }
  72140. break;
  72141. }
  72142. }
  72143. // }}}
  72144. $n = count($this->file_operations);
  72145. $this->log(2, "about to commit $n file operations for " . $this->pkginfo->getName());
  72146. $m = count($errors);
  72147. if ($m > 0) {
  72148. foreach ($errors as $error) {
  72149. if (!isset($this->_options['soft'])) {
  72150. $this->log(1, $error);
  72151. }
  72152. }
  72153. if (!isset($this->_options['ignore-errors'])) {
  72154. return false;
  72155. }
  72156. }
  72157. $this->_dirtree = array();
  72158. // {{{ really commit the transaction
  72159. foreach ($this->file_operations as $i => $tr) {
  72160. if (!$tr) {
  72161. // support removal of non-existing backups
  72162. continue;
  72163. }
  72164. list($type, $data) = $tr;
  72165. switch ($type) {
  72166. case 'backup':
  72167. if (!file_exists($data[0])) {
  72168. $this->file_operations[$i] = false;
  72169. break;
  72170. }
  72171. if (!@copy($data[0], $data[0] . '.bak')) {
  72172. $this->log(1, 'Could not copy ' . $data[0] . ' to ' . $data[0] .
  72173. '.bak ' . error_get_last()["message"]);
  72174. return false;
  72175. }
  72176. $this->log(3, "+ backup $data[0] to $data[0].bak");
  72177. break;
  72178. case 'removebackup':
  72179. if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
  72180. unlink($data[0] . '.bak');
  72181. $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
  72182. }
  72183. break;
  72184. case 'rename':
  72185. $test = file_exists($data[1]) ? @unlink($data[1]) : null;
  72186. if (!$test && file_exists($data[1])) {
  72187. if ($data[2]) {
  72188. $extra = ', this extension must be installed manually. Rename to "' .
  72189. basename($data[1]) . '"';
  72190. } else {
  72191. $extra = '';
  72192. }
  72193. if (!isset($this->_options['soft'])) {
  72194. $this->log(1, 'Could not delete ' . $data[1] . ', cannot rename ' .
  72195. $data[0] . $extra);
  72196. }
  72197. if (!isset($this->_options['ignore-errors'])) {
  72198. return false;
  72199. }
  72200. }
  72201. // permissions issues with rename - copy() is far superior
  72202. $perms = @fileperms($data[0]);
  72203. if (!@copy($data[0], $data[1])) {
  72204. $this->log(1, 'Could not rename ' . $data[0] . ' to ' . $data[1] .
  72205. ' ' . error_get_last()["message"]);
  72206. return false;
  72207. }
  72208. // copy over permissions, otherwise they are lost
  72209. @chmod($data[1], $perms);
  72210. @unlink($data[0]);
  72211. $this->log(3, "+ mv $data[0] $data[1]");
  72212. break;
  72213. case 'chmod':
  72214. if (!@chmod($data[1], $data[0])) {
  72215. $this->log(1, 'Could not chmod ' . $data[1] . ' to ' .
  72216. decoct($data[0]) . ' ' . error_get_last()["message"]);
  72217. return false;
  72218. }
  72219. $octmode = decoct($data[0]);
  72220. $this->log(3, "+ chmod $octmode $data[1]");
  72221. break;
  72222. case 'delete':
  72223. if (file_exists($data[0])) {
  72224. if (!@unlink($data[0])) {
  72225. $this->log(1, 'Could not delete ' . $data[0] . ' ' .
  72226. error_get_last()["message"]);
  72227. return false;
  72228. }
  72229. $this->log(3, "+ rm $data[0]");
  72230. }
  72231. break;
  72232. case 'rmdir':
  72233. if (file_exists($data[0])) {
  72234. do {
  72235. $testme = opendir($data[0]);
  72236. while (false !== ($entry = readdir($testme))) {
  72237. if ($entry == '.' || $entry == '..') {
  72238. continue;
  72239. }
  72240. closedir($testme);
  72241. break 2; // this directory is not empty and can't be
  72242. // deleted
  72243. }
  72244. closedir($testme);
  72245. if (!@rmdir($data[0])) {
  72246. $this->log(1, 'Could not rmdir ' . $data[0] . ' ' .
  72247. error_get_last()["message"]);
  72248. return false;
  72249. }
  72250. $this->log(3, "+ rmdir $data[0]");
  72251. } while (false);
  72252. }
  72253. break;
  72254. case 'installed_as':
  72255. $this->pkginfo->setInstalledAs($data[0], $data[1]);
  72256. if (!isset($this->_dirtree[dirname($data[1])])) {
  72257. $this->_dirtree[dirname($data[1])] = true;
  72258. $this->pkginfo->setDirtree(dirname($data[1]));
  72259. while(!empty($data[3]) && dirname($data[3]) != $data[3] &&
  72260. $data[3] != '/' && $data[3] != '\\') {
  72261. $this->pkginfo->setDirtree($pp =
  72262. $this->_prependPath($data[3], $data[2]));
  72263. $this->_dirtree[$pp] = true;
  72264. $data[3] = dirname($data[3]);
  72265. }
  72266. }
  72267. break;
  72268. }
  72269. }
  72270. // }}}
  72271. $this->log(2, "successfully committed $n file operations");
  72272. $this->file_operations = array();
  72273. return true;
  72274. }
  72275. // }}}
  72276. // {{{ rollbackFileTransaction()
  72277. function rollbackFileTransaction()
  72278. {
  72279. $n = count($this->file_operations);
  72280. $this->log(2, "rolling back $n file operations");
  72281. foreach ($this->file_operations as $tr) {
  72282. list($type, $data) = $tr;
  72283. switch ($type) {
  72284. case 'backup':
  72285. if (file_exists($data[0] . '.bak')) {
  72286. if (file_exists($data[0] && is_writable($data[0]))) {
  72287. unlink($data[0]);
  72288. }
  72289. @copy($data[0] . '.bak', $data[0]);
  72290. $this->log(3, "+ restore $data[0] from $data[0].bak");
  72291. }
  72292. break;
  72293. case 'removebackup':
  72294. if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
  72295. unlink($data[0] . '.bak');
  72296. $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
  72297. }
  72298. break;
  72299. case 'rename':
  72300. @unlink($data[0]);
  72301. $this->log(3, "+ rm $data[0]");
  72302. break;
  72303. case 'mkdir':
  72304. @rmdir($data[0]);
  72305. $this->log(3, "+ rmdir $data[0]");
  72306. break;
  72307. case 'chmod':
  72308. break;
  72309. case 'delete':
  72310. break;
  72311. case 'installed_as':
  72312. $this->pkginfo->setInstalledAs($data[0], false);
  72313. break;
  72314. }
  72315. }
  72316. $this->pkginfo->resetDirtree();
  72317. $this->file_operations = array();
  72318. }
  72319. // }}}
  72320. // {{{ mkDirHier($dir)
  72321. function mkDirHier($dir)
  72322. {
  72323. $this->addFileOperation('mkdir', array($dir));
  72324. return parent::mkDirHier($dir);
  72325. }
  72326. // }}}
  72327. // {{{ _parsePackageXml()
  72328. function _parsePackageXml(&$descfile)
  72329. {
  72330. // Parse xml file -----------------------------------------------
  72331. $pkg = new PEAR_PackageFile($this->config, $this->debug);
  72332. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  72333. $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING);
  72334. PEAR::staticPopErrorHandling();
  72335. if (PEAR::isError($p)) {
  72336. if (is_array($p->getUserInfo())) {
  72337. foreach ($p->getUserInfo() as $err) {
  72338. $loglevel = $err['level'] == 'error' ? 0 : 1;
  72339. if (!isset($this->_options['soft'])) {
  72340. $this->log($loglevel, ucfirst($err['level']) . ': ' . $err['message']);
  72341. }
  72342. }
  72343. }
  72344. return $this->raiseError('Installation failed: invalid package file');
  72345. }
  72346. $descfile = $p->getPackageFile();
  72347. return $p;
  72348. }
  72349. // }}}
  72350. /**
  72351. * Set the list of PEAR_Downloader_Package objects to allow more sane
  72352. * dependency validation
  72353. * @param array
  72354. */
  72355. function setDownloadedPackages(&$pkgs)
  72356. {
  72357. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  72358. $err = $this->analyzeDependencies($pkgs);
  72359. PEAR::popErrorHandling();
  72360. if (PEAR::isError($err)) {
  72361. return $err;
  72362. }
  72363. $this->_downloadedPackages = &$pkgs;
  72364. }
  72365. /**
  72366. * Set the list of PEAR_Downloader_Package objects to allow more sane
  72367. * dependency validation
  72368. * @param array
  72369. */
  72370. function setUninstallPackages(&$pkgs)
  72371. {
  72372. $this->_downloadedPackages = &$pkgs;
  72373. }
  72374. function getInstallPackages()
  72375. {
  72376. return $this->_downloadedPackages;
  72377. }
  72378. // {{{ install()
  72379. /**
  72380. * Installs the files within the package file specified.
  72381. *
  72382. * @param string|PEAR_Downloader_Package $pkgfile path to the package file,
  72383. * or a pre-initialized packagefile object
  72384. * @param array $options
  72385. * recognized options:
  72386. * - installroot : optional prefix directory for installation
  72387. * - force : force installation
  72388. * - register-only : update registry but don't install files
  72389. * - upgrade : upgrade existing install
  72390. * - soft : fail silently
  72391. * - nodeps : ignore dependency conflicts/missing dependencies
  72392. * - alldeps : install all dependencies
  72393. * - onlyreqdeps : install only required dependencies
  72394. *
  72395. * @return array|PEAR_Error package info if successful
  72396. */
  72397. function install($pkgfile, $options = array())
  72398. {
  72399. $this->_options = $options;
  72400. $this->_registry = &$this->config->getRegistry();
  72401. if (is_object($pkgfile)) {
  72402. $dlpkg = &$pkgfile;
  72403. $pkg = $pkgfile->getPackageFile();
  72404. $pkgfile = $pkg->getArchiveFile();
  72405. $descfile = $pkg->getPackageFile();
  72406. } else {
  72407. $descfile = $pkgfile;
  72408. $pkg = $this->_parsePackageXml($descfile);
  72409. if (PEAR::isError($pkg)) {
  72410. return $pkg;
  72411. }
  72412. }
  72413. $tmpdir = dirname($descfile);
  72414. if (realpath($descfile) != realpath($pkgfile)) {
  72415. // Use the temp_dir since $descfile can contain the download dir path
  72416. $tmpdir = $this->config->get('temp_dir', null, 'pear.php.net');
  72417. $tmpdir = System::mktemp('-d -t "' . $tmpdir . '"');
  72418. $tar = new Archive_Tar($pkgfile);
  72419. if (!$tar->extract($tmpdir)) {
  72420. return $this->raiseError("unable to unpack $pkgfile");
  72421. }
  72422. }
  72423. $pkgname = $pkg->getName();
  72424. $channel = $pkg->getChannel();
  72425. if (isset($options['installroot'])) {
  72426. $this->config->setInstallRoot($options['installroot']);
  72427. $this->_registry = &$this->config->getRegistry();
  72428. $installregistry = &$this->_registry;
  72429. $this->installroot = ''; // all done automagically now
  72430. $php_dir = $this->config->get('php_dir', null, $channel);
  72431. } else {
  72432. $this->config->setInstallRoot(false);
  72433. $this->_registry = &$this->config->getRegistry();
  72434. if (isset($this->_options['packagingroot'])) {
  72435. $regdir = $this->_prependPath(
  72436. $this->config->get('php_dir', null, 'pear.php.net'),
  72437. $this->_options['packagingroot']);
  72438. $metadata_dir = $this->config->get('metadata_dir', null, 'pear.php.net');
  72439. if ($metadata_dir) {
  72440. $metadata_dir = $this->_prependPath(
  72441. $metadata_dir,
  72442. $this->_options['packagingroot']);
  72443. }
  72444. $packrootphp_dir = $this->_prependPath(
  72445. $this->config->get('php_dir', null, $channel),
  72446. $this->_options['packagingroot']);
  72447. $installregistry = new PEAR_Registry($regdir, false, false, $metadata_dir);
  72448. if (!$installregistry->channelExists($channel, true)) {
  72449. // we need to fake a channel-discover of this channel
  72450. $chanobj = $this->_registry->getChannel($channel, true);
  72451. $installregistry->addChannel($chanobj);
  72452. }
  72453. $php_dir = $packrootphp_dir;
  72454. } else {
  72455. $installregistry = &$this->_registry;
  72456. $php_dir = $this->config->get('php_dir', null, $channel);
  72457. }
  72458. $this->installroot = '';
  72459. }
  72460. // {{{ checks to do when not in "force" mode
  72461. if (empty($options['force']) &&
  72462. (file_exists($this->config->get('php_dir')) &&
  72463. is_dir($this->config->get('php_dir')))) {
  72464. $testp = $channel == 'pear.php.net' ? $pkgname : array($channel, $pkgname);
  72465. $instfilelist = $pkg->getInstallationFileList(true);
  72466. if (PEAR::isError($instfilelist)) {
  72467. return $instfilelist;
  72468. }
  72469. // ensure we have the most accurate registry
  72470. $installregistry->flushFileMap();
  72471. $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1');
  72472. if (PEAR::isError($test)) {
  72473. return $test;
  72474. }
  72475. if (sizeof($test)) {
  72476. $pkgs = $this->getInstallPackages();
  72477. $found = false;
  72478. foreach ($pkgs as $param) {
  72479. if ($pkg->isSubpackageOf($param)) {
  72480. $found = true;
  72481. break;
  72482. }
  72483. }
  72484. if ($found) {
  72485. // subpackages can conflict with earlier versions of parent packages
  72486. $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel());
  72487. $tmp = $test;
  72488. foreach ($tmp as $file => $info) {
  72489. if (is_array($info)) {
  72490. if (strtolower($info[1]) == strtolower($param->getPackage()) &&
  72491. strtolower($info[0]) == strtolower($param->getChannel())
  72492. ) {
  72493. if (isset($parentreg['filelist'][$file])) {
  72494. unset($parentreg['filelist'][$file]);
  72495. } else{
  72496. $pos = strpos($file, '/');
  72497. $basedir = substr($file, 0, $pos);
  72498. $file2 = substr($file, $pos + 1);
  72499. if (isset($parentreg['filelist'][$file2]['baseinstalldir'])
  72500. && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir
  72501. ) {
  72502. unset($parentreg['filelist'][$file2]);
  72503. }
  72504. }
  72505. unset($test[$file]);
  72506. }
  72507. } else {
  72508. if (strtolower($param->getChannel()) != 'pear.php.net') {
  72509. continue;
  72510. }
  72511. if (strtolower($info) == strtolower($param->getPackage())) {
  72512. if (isset($parentreg['filelist'][$file])) {
  72513. unset($parentreg['filelist'][$file]);
  72514. } else{
  72515. $pos = strpos($file, '/');
  72516. $basedir = substr($file, 0, $pos);
  72517. $file2 = substr($file, $pos + 1);
  72518. if (isset($parentreg['filelist'][$file2]['baseinstalldir'])
  72519. && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir
  72520. ) {
  72521. unset($parentreg['filelist'][$file2]);
  72522. }
  72523. }
  72524. unset($test[$file]);
  72525. }
  72526. }
  72527. }
  72528. $pfk = new PEAR_PackageFile($this->config);
  72529. $parentpkg = &$pfk->fromArray($parentreg);
  72530. $installregistry->updatePackage2($parentpkg);
  72531. }
  72532. if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) {
  72533. $tmp = $test;
  72534. foreach ($tmp as $file => $info) {
  72535. if (is_string($info)) {
  72536. // pear.php.net packages are always stored as strings
  72537. if (strtolower($info) == strtolower($param->getPackage())) {
  72538. // upgrading existing package
  72539. unset($test[$file]);
  72540. }
  72541. }
  72542. }
  72543. }
  72544. if (count($test)) {
  72545. $msg = "$channel/$pkgname: conflicting files found:\n";
  72546. $longest = max(array_map("strlen", array_keys($test)));
  72547. $fmt = "%{$longest}s (%s)\n";
  72548. foreach ($test as $file => $info) {
  72549. if (!is_array($info)) {
  72550. $info = array('pear.php.net', $info);
  72551. }
  72552. $info = $info[0] . '/' . $info[1];
  72553. $msg .= sprintf($fmt, $file, $info);
  72554. }
  72555. if (!isset($options['ignore-errors'])) {
  72556. return $this->raiseError($msg);
  72557. }
  72558. if (!isset($options['soft'])) {
  72559. $this->log(0, "WARNING: $msg");
  72560. }
  72561. }
  72562. }
  72563. }
  72564. // }}}
  72565. $this->startFileTransaction();
  72566. $usechannel = $channel;
  72567. if ($channel == 'pecl.php.net') {
  72568. $test = $installregistry->packageExists($pkgname, $channel);
  72569. if (!$test) {
  72570. $test = $installregistry->packageExists($pkgname, 'pear.php.net');
  72571. $usechannel = 'pear.php.net';
  72572. }
  72573. } else {
  72574. $test = $installregistry->packageExists($pkgname, $channel);
  72575. }
  72576. if (empty($options['upgrade']) && empty($options['soft'])) {
  72577. // checks to do only when installing new packages
  72578. if (empty($options['force']) && $test) {
  72579. return $this->raiseError("$channel/$pkgname is already installed");
  72580. }
  72581. } else {
  72582. // Upgrade
  72583. if ($test) {
  72584. $v1 = $installregistry->packageInfo($pkgname, 'version', $usechannel);
  72585. $v2 = $pkg->getVersion();
  72586. $cmp = version_compare("$v1", "$v2", 'gt');
  72587. if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) {
  72588. return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)");
  72589. }
  72590. }
  72591. }
  72592. // Do cleanups for upgrade and install, remove old release's files first
  72593. if ($test && empty($options['register-only'])) {
  72594. // when upgrading, remove old release's files first:
  72595. if (PEAR::isError($err = $this->_deletePackageFiles($pkgname, $usechannel,
  72596. true))) {
  72597. if (!isset($options['ignore-errors'])) {
  72598. return $this->raiseError($err);
  72599. }
  72600. if (!isset($options['soft'])) {
  72601. $this->log(0, 'WARNING: ' . $err->getMessage());
  72602. }
  72603. } else {
  72604. $backedup = $err;
  72605. }
  72606. }
  72607. // {{{ Copy files to dest dir ---------------------------------------
  72608. // info from the package it self we want to access from _installFile
  72609. $this->pkginfo = &$pkg;
  72610. // used to determine whether we should build any C code
  72611. $this->source_files = 0;
  72612. $savechannel = $this->config->get('default_channel');
  72613. if (empty($options['register-only']) && !is_dir($php_dir)) {
  72614. if (PEAR::isError(System::mkdir(array('-p'), $php_dir))) {
  72615. return $this->raiseError("no installation destination directory '$php_dir'\n");
  72616. }
  72617. }
  72618. if (substr($pkgfile, -4) != '.xml') {
  72619. $tmpdir .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkg->getVersion();
  72620. }
  72621. $this->configSet('default_channel', $channel);
  72622. // {{{ install files
  72623. $ver = $pkg->getPackagexmlVersion();
  72624. if (version_compare($ver, '2.0', '>=')) {
  72625. $filelist = $pkg->getInstallationFilelist();
  72626. } else {
  72627. $filelist = $pkg->getFileList();
  72628. }
  72629. if (PEAR::isError($filelist)) {
  72630. return $filelist;
  72631. }
  72632. $p = &$installregistry->getPackage($pkgname, $channel);
  72633. $dirtree = (empty($options['register-only']) && $p) ? $p->getDirTree() : false;
  72634. $pkg->resetFilelist();
  72635. $pkg->setLastInstalledVersion($installregistry->packageInfo($pkg->getPackage(),
  72636. 'version', $pkg->getChannel()));
  72637. foreach ($filelist as $file => $atts) {
  72638. $this->expectError(PEAR_INSTALLER_FAILED);
  72639. if ($pkg->getPackagexmlVersion() == '1.0') {
  72640. $res = $this->_installFile($file, $atts, $tmpdir, $options);
  72641. } else {
  72642. $res = $this->_installFile2($pkg, $file, $atts, $tmpdir, $options);
  72643. }
  72644. $this->popExpect();
  72645. if (PEAR::isError($res)) {
  72646. if (empty($options['ignore-errors'])) {
  72647. $this->rollbackFileTransaction();
  72648. if ($res->getMessage() == "file does not exist") {
  72649. $this->raiseError("file $file in package.xml does not exist");
  72650. }
  72651. return $this->raiseError($res);
  72652. }
  72653. if (!isset($options['soft'])) {
  72654. $this->log(0, "Warning: " . $res->getMessage());
  72655. }
  72656. }
  72657. $real = isset($atts['attribs']) ? $atts['attribs'] : $atts;
  72658. if ($res == PEAR_INSTALLER_OK && $real['role'] != 'src') {
  72659. // Register files that were installed
  72660. $pkg->installedFile($file, $atts);
  72661. }
  72662. }
  72663. // }}}
  72664. // {{{ compile and install source files
  72665. if ($this->source_files > 0 && empty($options['nobuild'])) {
  72666. $configureoptions = empty($options['configureoptions']) ? '' : $options['configureoptions'];
  72667. if (PEAR::isError($err =
  72668. $this->_compileSourceFiles($savechannel, $pkg, $configureoptions))) {
  72669. return $err;
  72670. }
  72671. }
  72672. // }}}
  72673. if (isset($backedup)) {
  72674. $this->_removeBackups($backedup);
  72675. }
  72676. if (!$this->commitFileTransaction()) {
  72677. $this->rollbackFileTransaction();
  72678. $this->configSet('default_channel', $savechannel);
  72679. return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED);
  72680. }
  72681. // }}}
  72682. $ret = false;
  72683. $installphase = 'install';
  72684. $oldversion = false;
  72685. // {{{ Register that the package is installed -----------------------
  72686. if (empty($options['upgrade'])) {
  72687. // if 'force' is used, replace the info in registry
  72688. $usechannel = $channel;
  72689. if ($channel == 'pecl.php.net') {
  72690. $test = $installregistry->packageExists($pkgname, $channel);
  72691. if (!$test) {
  72692. $test = $installregistry->packageExists($pkgname, 'pear.php.net');
  72693. $usechannel = 'pear.php.net';
  72694. }
  72695. } else {
  72696. $test = $installregistry->packageExists($pkgname, $channel);
  72697. }
  72698. if (!empty($options['force']) && $test) {
  72699. $oldversion = $installregistry->packageInfo($pkgname, 'version', $usechannel);
  72700. $installregistry->deletePackage($pkgname, $usechannel);
  72701. }
  72702. $ret = $installregistry->addPackage2($pkg);
  72703. } else {
  72704. if ($dirtree) {
  72705. $this->startFileTransaction();
  72706. // attempt to delete empty directories
  72707. uksort($dirtree, array($this, '_sortDirs'));
  72708. foreach($dirtree as $dir => $notused) {
  72709. $this->addFileOperation('rmdir', array($dir));
  72710. }
  72711. $this->commitFileTransaction();
  72712. }
  72713. $usechannel = $channel;
  72714. if ($channel == 'pecl.php.net') {
  72715. $test = $installregistry->packageExists($pkgname, $channel);
  72716. if (!$test) {
  72717. $test = $installregistry->packageExists($pkgname, 'pear.php.net');
  72718. $usechannel = 'pear.php.net';
  72719. }
  72720. } else {
  72721. $test = $installregistry->packageExists($pkgname, $channel);
  72722. }
  72723. // new: upgrade installs a package if it isn't installed
  72724. if (!$test) {
  72725. $ret = $installregistry->addPackage2($pkg);
  72726. } else {
  72727. if ($usechannel != $channel) {
  72728. $installregistry->deletePackage($pkgname, $usechannel);
  72729. $ret = $installregistry->addPackage2($pkg);
  72730. } else {
  72731. $ret = $installregistry->updatePackage2($pkg);
  72732. }
  72733. $installphase = 'upgrade';
  72734. }
  72735. }
  72736. if (!$ret) {
  72737. $this->configSet('default_channel', $savechannel);
  72738. return $this->raiseError("Adding package $channel/$pkgname to registry failed");
  72739. }
  72740. // }}}
  72741. $this->configSet('default_channel', $savechannel);
  72742. if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist
  72743. if (PEAR_Task_Common::hasPostinstallTasks()) {
  72744. PEAR_Task_Common::runPostinstallTasks($installphase);
  72745. }
  72746. }
  72747. return $pkg->toArray(true);
  72748. }
  72749. // }}}
  72750. // {{{ _compileSourceFiles()
  72751. /**
  72752. * @param string
  72753. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  72754. * @param mixed[] $configureoptions
  72755. */
  72756. function _compileSourceFiles($savechannel, &$filelist, $configureoptions)
  72757. {
  72758. require_once 'phar://go-pear.phar/' . 'PEAR/Builder.php';
  72759. $this->log(1, "$this->source_files source files, building");
  72760. $bob = new PEAR_Builder($configureoptions, $this->ui);
  72761. $bob->debug = $this->debug;
  72762. $built = $bob->build($filelist, array(&$this, '_buildCallback'));
  72763. if (PEAR::isError($built)) {
  72764. $this->rollbackFileTransaction();
  72765. $this->configSet('default_channel', $savechannel);
  72766. return $built;
  72767. }
  72768. $this->log(1, "\nBuild process completed successfully");
  72769. foreach ($built as $ext) {
  72770. $bn = basename($ext['file']);
  72771. list($_ext_name, $_ext_suff) = explode('.', $bn);
  72772. if ($_ext_suff == 'so' || $_ext_suff == 'dll') {
  72773. if (extension_loaded($_ext_name)) {
  72774. return $this->raiseError("Extension '$_ext_name' already loaded. " .
  72775. 'Please unload it in your php.ini file ' .
  72776. 'prior to install or upgrade');
  72777. }
  72778. $role = 'ext';
  72779. } else {
  72780. $role = 'src';
  72781. }
  72782. $dest = $ext['dest'];
  72783. $packagingroot = '';
  72784. if (isset($this->_options['packagingroot'])) {
  72785. $packagingroot = $this->_options['packagingroot'];
  72786. }
  72787. $copyto = $this->_prependPath($dest, $packagingroot);
  72788. $extra = $copyto != $dest ? " as '$copyto'" : '';
  72789. $this->log(1, "Installing '$dest'$extra");
  72790. $copydir = dirname($copyto);
  72791. // pretty much nothing happens if we are only registering the install
  72792. if (empty($this->_options['register-only'])) {
  72793. if (!file_exists($copydir) || !is_dir($copydir)) {
  72794. if (!$this->mkDirHier($copydir)) {
  72795. return $this->raiseError("failed to mkdir $copydir",
  72796. PEAR_INSTALLER_FAILED);
  72797. }
  72798. $this->log(3, "+ mkdir $copydir");
  72799. }
  72800. if (!@copy($ext['file'], $copyto)) {
  72801. return $this->raiseError(
  72802. "failed to write $copyto (" . error_get_last()["message"] . ")",
  72803. PEAR_INSTALLER_FAILED);
  72804. }
  72805. $this->log(3, "+ cp $ext[file] $copyto");
  72806. $this->addFileOperation('rename', array($ext['file'], $copyto));
  72807. if (!OS_WINDOWS) {
  72808. $mode = 0666 & ~(int)octdec($this->config->get('umask'));
  72809. $this->addFileOperation('chmod', array($mode, $copyto));
  72810. if (!@chmod($copyto, $mode)) {
  72811. $this->log(0, "failed to change mode of $copyto (" .
  72812. error_get_last()["message"] . ")");
  72813. }
  72814. }
  72815. }
  72816. $data = array(
  72817. 'role' => $role,
  72818. 'name' => $bn,
  72819. 'installed_as' => $dest,
  72820. 'php_api' => $ext['php_api'],
  72821. 'zend_mod_api' => $ext['zend_mod_api'],
  72822. 'zend_ext_api' => $ext['zend_ext_api'],
  72823. );
  72824. if ($filelist->getPackageXmlVersion() == '1.0') {
  72825. $filelist->installedFile($bn, $data);
  72826. } else {
  72827. $filelist->installedFile($bn, array('attribs' => $data));
  72828. }
  72829. }
  72830. }
  72831. // }}}
  72832. function &getUninstallPackages()
  72833. {
  72834. return $this->_downloadedPackages;
  72835. }
  72836. // {{{ uninstall()
  72837. /**
  72838. * Uninstall a package
  72839. *
  72840. * This method removes all files installed by the application, and then
  72841. * removes any empty directories.
  72842. * @param string package name
  72843. * @param array Command-line options. Possibilities include:
  72844. *
  72845. * - installroot: base installation dir, if not the default
  72846. * - register-only : update registry but don't remove files
  72847. * - nodeps: do not process dependencies of other packages to ensure
  72848. * uninstallation does not break things
  72849. */
  72850. function uninstall($package, $options = array())
  72851. {
  72852. $installRoot = isset($options['installroot']) ? $options['installroot'] : '';
  72853. $this->config->setInstallRoot($installRoot);
  72854. $this->installroot = '';
  72855. $this->_registry = &$this->config->getRegistry();
  72856. if (is_object($package)) {
  72857. $channel = $package->getChannel();
  72858. $pkg = $package;
  72859. $package = $pkg->getPackage();
  72860. } else {
  72861. $pkg = false;
  72862. $info = $this->_registry->parsePackageName($package,
  72863. $this->config->get('default_channel'));
  72864. $channel = $info['channel'];
  72865. $package = $info['package'];
  72866. }
  72867. $savechannel = $this->config->get('default_channel');
  72868. $this->configSet('default_channel', $channel);
  72869. if (!is_object($pkg)) {
  72870. $pkg = $this->_registry->getPackage($package, $channel);
  72871. }
  72872. if (!$pkg) {
  72873. $this->configSet('default_channel', $savechannel);
  72874. return $this->raiseError($this->_registry->parsedPackageNameToString(
  72875. array(
  72876. 'channel' => $channel,
  72877. 'package' => $package
  72878. ), true) . ' not installed');
  72879. }
  72880. if ($pkg->getInstalledBinary()) {
  72881. // this is just an alias for a binary package
  72882. return $this->_registry->deletePackage($package, $channel);
  72883. }
  72884. $filelist = $pkg->getFilelist();
  72885. PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  72886. if (!class_exists('PEAR_Dependency2')) {
  72887. require_once 'phar://go-pear.phar/' . 'PEAR/Dependency2.php';
  72888. }
  72889. $depchecker = new PEAR_Dependency2($this->config, $options,
  72890. array('channel' => $channel, 'package' => $package),
  72891. PEAR_VALIDATE_UNINSTALLING);
  72892. $e = $depchecker->validatePackageUninstall($this);
  72893. PEAR::staticPopErrorHandling();
  72894. if (PEAR::isError($e)) {
  72895. if (!isset($options['ignore-errors'])) {
  72896. return $this->raiseError($e);
  72897. }
  72898. if (!isset($options['soft'])) {
  72899. $this->log(0, 'WARNING: ' . $e->getMessage());
  72900. }
  72901. } elseif (is_array($e)) {
  72902. if (!isset($options['soft'])) {
  72903. $this->log(0, $e[0]);
  72904. }
  72905. }
  72906. $this->pkginfo = &$pkg;
  72907. // pretty much nothing happens if we are only registering the uninstall
  72908. if (empty($options['register-only'])) {
  72909. // {{{ Delete the files
  72910. $this->startFileTransaction();
  72911. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  72912. if (PEAR::isError($err = $this->_deletePackageFiles($package, $channel))) {
  72913. PEAR::popErrorHandling();
  72914. $this->rollbackFileTransaction();
  72915. $this->configSet('default_channel', $savechannel);
  72916. if (!isset($options['ignore-errors'])) {
  72917. return $this->raiseError($err);
  72918. }
  72919. if (!isset($options['soft'])) {
  72920. $this->log(0, 'WARNING: ' . $err->getMessage());
  72921. }
  72922. } else {
  72923. PEAR::popErrorHandling();
  72924. }
  72925. if (!$this->commitFileTransaction()) {
  72926. $this->rollbackFileTransaction();
  72927. if (!isset($options['ignore-errors'])) {
  72928. return $this->raiseError("uninstall failed");
  72929. }
  72930. if (!isset($options['soft'])) {
  72931. $this->log(0, 'WARNING: uninstall failed');
  72932. }
  72933. } else {
  72934. $this->startFileTransaction();
  72935. $dirtree = $pkg->getDirTree();
  72936. if ($dirtree === false) {
  72937. $this->configSet('default_channel', $savechannel);
  72938. return $this->_registry->deletePackage($package, $channel);
  72939. }
  72940. // attempt to delete empty directories
  72941. uksort($dirtree, array($this, '_sortDirs'));
  72942. foreach($dirtree as $dir => $notused) {
  72943. $this->addFileOperation('rmdir', array($dir));
  72944. }
  72945. if (!$this->commitFileTransaction()) {
  72946. $this->rollbackFileTransaction();
  72947. if (!isset($options['ignore-errors'])) {
  72948. return $this->raiseError("uninstall failed");
  72949. }
  72950. if (!isset($options['soft'])) {
  72951. $this->log(0, 'WARNING: uninstall failed');
  72952. }
  72953. }
  72954. }
  72955. // }}}
  72956. }
  72957. $this->configSet('default_channel', $savechannel);
  72958. // Register that the package is no longer installed
  72959. return $this->_registry->deletePackage($package, $channel);
  72960. }
  72961. /**
  72962. * Sort a list of arrays of array(downloaded packagefilename) by dependency.
  72963. *
  72964. * It also removes duplicate dependencies
  72965. * @param array an array of PEAR_PackageFile_v[1/2] objects
  72966. * @return array|PEAR_Error array of array(packagefilename, package.xml contents)
  72967. */
  72968. function sortPackagesForUninstall(&$packages)
  72969. {
  72970. $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->config);
  72971. if (PEAR::isError($this->_dependencyDB)) {
  72972. return $this->_dependencyDB;
  72973. }
  72974. usort($packages, array(&$this, '_sortUninstall'));
  72975. }
  72976. function _sortUninstall($a, $b)
  72977. {
  72978. if (!$a->getDeps() && !$b->getDeps()) {
  72979. return 0; // neither package has dependencies, order is insignificant
  72980. }
  72981. if ($a->getDeps() && !$b->getDeps()) {
  72982. return -1; // $a must be installed after $b because $a has dependencies
  72983. }
  72984. if (!$a->getDeps() && $b->getDeps()) {
  72985. return 1; // $b must be installed after $a because $b has dependencies
  72986. }
  72987. // both packages have dependencies
  72988. if ($this->_dependencyDB->dependsOn($a, $b)) {
  72989. return -1;
  72990. }
  72991. if ($this->_dependencyDB->dependsOn($b, $a)) {
  72992. return 1;
  72993. }
  72994. return 0;
  72995. }
  72996. // }}}
  72997. // {{{ _sortDirs()
  72998. function _sortDirs($a, $b)
  72999. {
  73000. if (strnatcmp($a, $b) == -1) return 1;
  73001. if (strnatcmp($a, $b) == 1) return -1;
  73002. return 0;
  73003. }
  73004. // }}}
  73005. // {{{ _buildCallback()
  73006. function _buildCallback($what, $data)
  73007. {
  73008. if (($what == 'cmdoutput' && $this->debug > 1) ||
  73009. ($what == 'output' && $this->debug > 0)) {
  73010. $this->ui->outputData(rtrim($data), 'build');
  73011. }
  73012. }
  73013. // }}}
  73014. }
  73015. <?php
  73016. /**
  73017. * PEAR_Installer_Role
  73018. *
  73019. * PHP versions 4 and 5
  73020. *
  73021. * @category pear
  73022. * @package PEAR
  73023. * @author Greg Beaver <cellog@php.net>
  73024. * @copyright 1997-2009 The Authors
  73025. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  73026. * @link http://pear.php.net/package/PEAR
  73027. * @since File available since Release 1.4.0a1
  73028. */
  73029. /**
  73030. * base class for installer roles
  73031. */
  73032. require_once 'phar://go-pear.phar/' . 'PEAR/Installer/Role/Common.php';
  73033. require_once 'phar://go-pear.phar/' . 'PEAR/XMLParser.php';
  73034. /**
  73035. * @category pear
  73036. * @package PEAR
  73037. * @author Greg Beaver <cellog@php.net>
  73038. * @copyright 1997-2009 The Authors
  73039. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  73040. * @version Release: 1.10.16
  73041. * @link http://pear.php.net/package/PEAR
  73042. * @since Class available since Release 1.4.0a1
  73043. */
  73044. class PEAR_Installer_Role
  73045. {
  73046. /**
  73047. * Set up any additional configuration variables that file roles require
  73048. *
  73049. * Never call this directly, it is called by the PEAR_Config constructor
  73050. * @param PEAR_Config
  73051. */
  73052. public static function initializeConfig(&$config)
  73053. {
  73054. if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
  73055. PEAR_Installer_Role::registerRoles();
  73056. }
  73057. foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $class => $info) {
  73058. if (!$info['config_vars']) {
  73059. continue;
  73060. }
  73061. $config->_addConfigVars($class, $info['config_vars']);
  73062. }
  73063. }
  73064. /**
  73065. * @param PEAR_PackageFile_v2
  73066. * @param string role name
  73067. * @param PEAR_Config
  73068. * @return PEAR_Installer_Role_Common
  73069. */
  73070. public static function &factory($pkg, $role, &$config)
  73071. {
  73072. if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
  73073. PEAR_Installer_Role::registerRoles();
  73074. }
  73075. if (!in_array($role, PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
  73076. $a = false;
  73077. return $a;
  73078. }
  73079. $a = 'PEAR_Installer_Role_' . ucfirst($role);
  73080. if (!class_exists($a)) {
  73081. require_once 'phar://go-pear.phar/' . str_replace('_', '/', $a) . '.php';
  73082. }
  73083. $b = new $a($config);
  73084. return $b;
  73085. }
  73086. /**
  73087. * Get a list of file roles that are valid for the particular release type.
  73088. *
  73089. * For instance, src files serve no purpose in regular php releases.
  73090. * @param string
  73091. * @param bool clear cache
  73092. * @return array
  73093. */
  73094. public static function getValidRoles($release, $clear = false)
  73095. {
  73096. if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
  73097. PEAR_Installer_Role::registerRoles();
  73098. }
  73099. static $ret = array();
  73100. if ($clear) {
  73101. $ret = array();
  73102. }
  73103. if (isset($ret[$release])) {
  73104. return $ret[$release];
  73105. }
  73106. $ret[$release] = array();
  73107. foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
  73108. if (in_array($release, $okreleases['releasetypes'])) {
  73109. $ret[$release][] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
  73110. }
  73111. }
  73112. return $ret[$release];
  73113. }
  73114. /**
  73115. * Get a list of roles that require their files to be installed
  73116. *
  73117. * Most roles must be installed, but src and package roles, for instance
  73118. * are pseudo-roles. src files are compiled into a new extension. Package
  73119. * roles are actually fully bundled releases of a package
  73120. * @param bool clear cache
  73121. * @return array
  73122. */
  73123. public static function getInstallableRoles($clear = false)
  73124. {
  73125. if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
  73126. PEAR_Installer_Role::registerRoles();
  73127. }
  73128. static $ret;
  73129. if ($clear) {
  73130. unset($ret);
  73131. }
  73132. if (isset($ret)) {
  73133. return $ret;
  73134. }
  73135. $ret = array();
  73136. foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
  73137. if ($okreleases['installable']) {
  73138. $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
  73139. }
  73140. }
  73141. return $ret;
  73142. }
  73143. /**
  73144. * Return an array of roles that are affected by the baseinstalldir attribute
  73145. *
  73146. * Most roles ignore this attribute, and instead install directly into:
  73147. * PackageName/filepath
  73148. * so a tests file tests/file.phpt is installed into PackageName/tests/filepath.php
  73149. * @param bool clear cache
  73150. * @return array
  73151. */
  73152. public static function getBaseinstallRoles($clear = false)
  73153. {
  73154. if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
  73155. PEAR_Installer_Role::registerRoles();
  73156. }
  73157. static $ret;
  73158. if ($clear) {
  73159. unset($ret);
  73160. }
  73161. if (isset($ret)) {
  73162. return $ret;
  73163. }
  73164. $ret = array();
  73165. foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
  73166. if ($okreleases['honorsbaseinstall']) {
  73167. $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
  73168. }
  73169. }
  73170. return $ret;
  73171. }
  73172. /**
  73173. * Return an array of file roles that should be analyzed for PHP content at package time,
  73174. * like the "php" role.
  73175. * @param bool clear cache
  73176. * @return array
  73177. */
  73178. public static function getPhpRoles($clear = false)
  73179. {
  73180. if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'])) {
  73181. PEAR_Installer_Role::registerRoles();
  73182. }
  73183. static $ret;
  73184. if ($clear) {
  73185. unset($ret);
  73186. }
  73187. if (isset($ret)) {
  73188. return $ret;
  73189. }
  73190. $ret = array();
  73191. foreach ($GLOBALS['_PEAR_INSTALLER_ROLES'] as $role => $okreleases) {
  73192. if ($okreleases['phpfile']) {
  73193. $ret[] = strtolower(str_replace('PEAR_Installer_Role_', '', $role));
  73194. }
  73195. }
  73196. return $ret;
  73197. }
  73198. /**
  73199. * Scan through the Command directory looking for classes
  73200. * and see what commands they implement.
  73201. * @param string which directory to look for classes, defaults to
  73202. * the Installer/Roles subdirectory of
  73203. * the directory from where this file (__FILE__) is
  73204. * included.
  73205. *
  73206. * @return bool TRUE on success, a PEAR error on failure
  73207. */
  73208. public static function registerRoles($dir = null)
  73209. {
  73210. $GLOBALS['_PEAR_INSTALLER_ROLES'] = array();
  73211. $parser = new PEAR_XMLParser;
  73212. if ($dir === null) {
  73213. $dir = dirname(__FILE__) . '/Role';
  73214. }
  73215. if (!file_exists($dir) || !is_dir($dir)) {
  73216. return PEAR::raiseError("registerRoles: opendir($dir) failed: does not exist/is not directory");
  73217. }
  73218. $dp = @opendir($dir);
  73219. if (empty($dp)) {
  73220. return PEAR::raiseError("registerRoles: opendir($dir) failed: $php_errmsg");
  73221. }
  73222. while ($entry = readdir($dp)) {
  73223. if ($entry[0] == '.' || substr($entry, -4) != '.xml') {
  73224. continue;
  73225. }
  73226. $class = "PEAR_Installer_Role_".substr($entry, 0, -4);
  73227. // List of roles
  73228. if (!isset($GLOBALS['_PEAR_INSTALLER_ROLES'][$class])) {
  73229. $file = "$dir/$entry";
  73230. $parser->parse(file_get_contents($file));
  73231. $data = $parser->getData();
  73232. if (!is_array($data['releasetypes'])) {
  73233. $data['releasetypes'] = array($data['releasetypes']);
  73234. }
  73235. $GLOBALS['_PEAR_INSTALLER_ROLES'][$class] = $data;
  73236. }
  73237. }
  73238. closedir($dp);
  73239. ksort($GLOBALS['_PEAR_INSTALLER_ROLES']);
  73240. PEAR_Installer_Role::getBaseinstallRoles(true);
  73241. PEAR_Installer_Role::getInstallableRoles(true);
  73242. PEAR_Installer_Role::getPhpRoles(true);
  73243. PEAR_Installer_Role::getValidRoles('****', true);
  73244. return true;
  73245. }
  73246. }<?php
  73247. /**
  73248. * Base class for all installation roles.
  73249. *
  73250. * PHP versions 4 and 5
  73251. *
  73252. * @category pear
  73253. * @package PEAR
  73254. * @author Greg Beaver <cellog@php.net>
  73255. * @copyright 1997-2006 The PHP Group
  73256. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  73257. * @link http://pear.php.net/package/PEAR
  73258. * @since File available since Release 1.4.0a1
  73259. */
  73260. /**
  73261. * Base class for all installation roles.
  73262. *
  73263. * This class allows extensibility of file roles. Packages with complex
  73264. * customization can now provide custom file roles along with the possibility of
  73265. * adding configuration values to match.
  73266. * @category pear
  73267. * @package PEAR
  73268. * @author Greg Beaver <cellog@php.net>
  73269. * @copyright 1997-2006 The PHP Group
  73270. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  73271. * @version Release: 1.10.16
  73272. * @link http://pear.php.net/package/PEAR
  73273. * @since Class available since Release 1.4.0a1
  73274. */
  73275. class PEAR_Installer_Role_Common
  73276. {
  73277. /**
  73278. * @var PEAR_Config
  73279. * @access protected
  73280. */
  73281. var $config;
  73282. /**
  73283. * @param PEAR_Config
  73284. */
  73285. function __construct(&$config)
  73286. {
  73287. $this->config = $config;
  73288. }
  73289. /**
  73290. * Retrieve configuration information about a file role from its XML info
  73291. *
  73292. * @param string $role Role Classname, as in "PEAR_Installer_Role_Data"
  73293. * @return array
  73294. */
  73295. function getInfo($role)
  73296. {
  73297. if (empty($GLOBALS['_PEAR_INSTALLER_ROLES'][$role])) {
  73298. return PEAR::raiseError('Unknown Role class: "' . $role . '"');
  73299. }
  73300. return $GLOBALS['_PEAR_INSTALLER_ROLES'][$role];
  73301. }
  73302. /**
  73303. * This is called for each file to set up the directories and files
  73304. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  73305. * @param array attributes from the <file> tag
  73306. * @param string file name
  73307. * @return array an array consisting of:
  73308. *
  73309. * 1 the original, pre-baseinstalldir installation directory
  73310. * 2 the final installation directory
  73311. * 3 the full path to the final location of the file
  73312. * 4 the location of the pre-installation file
  73313. */
  73314. function processInstallation($pkg, $atts, $file, $tmp_path, $layer = null)
  73315. {
  73316. $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' .
  73317. ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
  73318. if (PEAR::isError($roleInfo)) {
  73319. return $roleInfo;
  73320. }
  73321. if (!$roleInfo['locationconfig']) {
  73322. return false;
  73323. }
  73324. if ($roleInfo['honorsbaseinstall']) {
  73325. $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'], $layer,
  73326. $pkg->getChannel());
  73327. if (!empty($atts['baseinstalldir'])) {
  73328. $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
  73329. }
  73330. } elseif ($roleInfo['unusualbaseinstall']) {
  73331. $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'],
  73332. $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage();
  73333. if (!empty($atts['baseinstalldir'])) {
  73334. $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
  73335. }
  73336. } else {
  73337. $dest_dir = $save_destdir = $this->config->get($roleInfo['locationconfig'],
  73338. $layer, $pkg->getChannel()) . DIRECTORY_SEPARATOR . $pkg->getPackage();
  73339. }
  73340. if (dirname($file) != '.' && empty($atts['install-as'])) {
  73341. $dest_dir .= DIRECTORY_SEPARATOR . dirname($file);
  73342. }
  73343. if (empty($atts['install-as'])) {
  73344. $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file);
  73345. } else {
  73346. $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as'];
  73347. }
  73348. $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file;
  73349. // Clean up the DIRECTORY_SEPARATOR mess
  73350. $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
  73351. list($dest_dir, $dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
  73352. array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR,
  73353. DIRECTORY_SEPARATOR),
  73354. array($dest_dir, $dest_file, $orig_file));
  73355. return array($save_destdir, $dest_dir, $dest_file, $orig_file);
  73356. }
  73357. /**
  73358. * Get the name of the configuration variable that specifies the location of this file
  73359. * @return string|false
  73360. */
  73361. function getLocationConfig()
  73362. {
  73363. $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' .
  73364. ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
  73365. if (PEAR::isError($roleInfo)) {
  73366. return $roleInfo;
  73367. }
  73368. return $roleInfo['locationconfig'];
  73369. }
  73370. /**
  73371. * Do any unusual setup here
  73372. * @param PEAR_Installer
  73373. * @param PEAR_PackageFile_v2
  73374. * @param array file attributes
  73375. * @param string file name
  73376. */
  73377. function setup(&$installer, $pkg, $atts, $file)
  73378. {
  73379. }
  73380. function isExecutable()
  73381. {
  73382. $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' .
  73383. ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
  73384. if (PEAR::isError($roleInfo)) {
  73385. return $roleInfo;
  73386. }
  73387. return $roleInfo['executable'];
  73388. }
  73389. function isInstallable()
  73390. {
  73391. $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' .
  73392. ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
  73393. if (PEAR::isError($roleInfo)) {
  73394. return $roleInfo;
  73395. }
  73396. return $roleInfo['installable'];
  73397. }
  73398. function isExtension()
  73399. {
  73400. $roleInfo = PEAR_Installer_Role_Common::getInfo('PEAR_Installer_Role_' .
  73401. ucfirst(str_replace('pear_installer_role_', '', strtolower(get_class($this)))));
  73402. if (PEAR::isError($roleInfo)) {
  73403. return $roleInfo;
  73404. }
  73405. return $roleInfo['phpextension'];
  73406. }
  73407. }
  73408. ?>
  73409. <?php
  73410. /**
  73411. * PEAR_Installer_Role_Data
  73412. *
  73413. * PHP versions 4 and 5
  73414. *
  73415. * @category pear
  73416. * @package PEAR
  73417. * @author Greg Beaver <cellog@php.net>
  73418. * @copyright 1997-2009 The Authors
  73419. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  73420. * @link http://pear.php.net/package/PEAR
  73421. * @since File available since Release 1.4.0a1
  73422. */
  73423. /**
  73424. * @category pear
  73425. * @package PEAR
  73426. * @author Greg Beaver <cellog@php.net>
  73427. * @copyright 1997-2009 The Authors
  73428. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  73429. * @version Release: 1.10.16
  73430. * @link http://pear.php.net/package/PEAR
  73431. * @since Class available since Release 1.4.0a1
  73432. */
  73433. class PEAR_Installer_Role_Data extends PEAR_Installer_Role_Common {}
  73434. ?><role version="1.0">
  73435. <releasetypes>php</releasetypes>
  73436. <releasetypes>extsrc</releasetypes>
  73437. <releasetypes>extbin</releasetypes>
  73438. <releasetypes>zendextsrc</releasetypes>
  73439. <releasetypes>zendextbin</releasetypes>
  73440. <installable>1</installable>
  73441. <locationconfig>data_dir</locationconfig>
  73442. <honorsbaseinstall />
  73443. <unusualbaseinstall />
  73444. <phpfile />
  73445. <executable />
  73446. <phpextension />
  73447. <config_vars />
  73448. </role><?php
  73449. /**
  73450. * PEAR_Installer_Role_Doc
  73451. *
  73452. * PHP versions 4 and 5
  73453. *
  73454. * @category pear
  73455. * @package PEAR
  73456. * @author Greg Beaver <cellog@php.net>
  73457. * @copyright 1997-2009 The Authors
  73458. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  73459. * @link http://pear.php.net/package/PEAR
  73460. * @since File available since Release 1.4.0a1
  73461. */
  73462. /**
  73463. * @category pear
  73464. * @package PEAR
  73465. * @author Greg Beaver <cellog@php.net>
  73466. * @copyright 1997-2009 The Authors
  73467. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  73468. * @version Release: 1.10.16
  73469. * @link http://pear.php.net/package/PEAR
  73470. * @since Class available since Release 1.4.0a1
  73471. */
  73472. class PEAR_Installer_Role_Doc extends PEAR_Installer_Role_Common {}
  73473. ?><role version="1.0">
  73474. <releasetypes>php</releasetypes>
  73475. <releasetypes>extsrc</releasetypes>
  73476. <releasetypes>extbin</releasetypes>
  73477. <releasetypes>zendextsrc</releasetypes>
  73478. <releasetypes>zendextbin</releasetypes>
  73479. <installable>1</installable>
  73480. <locationconfig>doc_dir</locationconfig>
  73481. <honorsbaseinstall />
  73482. <unusualbaseinstall />
  73483. <phpfile />
  73484. <executable />
  73485. <phpextension />
  73486. <config_vars />
  73487. </role><?php
  73488. /**
  73489. * PEAR_Installer_Role_Php
  73490. *
  73491. * PHP versions 4 and 5
  73492. *
  73493. * @category pear
  73494. * @package PEAR
  73495. * @author Greg Beaver <cellog@php.net>
  73496. * @copyright 1997-2009 The Authors
  73497. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  73498. * @link http://pear.php.net/package/PEAR
  73499. * @since File available since Release 1.4.0a1
  73500. */
  73501. /**
  73502. * @category pear
  73503. * @package PEAR
  73504. * @author Greg Beaver <cellog@php.net>
  73505. * @copyright 1997-2009 The Authors
  73506. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  73507. * @version Release: 1.10.16
  73508. * @link http://pear.php.net/package/PEAR
  73509. * @since Class available since Release 1.4.0a1
  73510. */
  73511. class PEAR_Installer_Role_Php extends PEAR_Installer_Role_Common {}
  73512. ?><role version="1.0">
  73513. <releasetypes>php</releasetypes>
  73514. <releasetypes>extsrc</releasetypes>
  73515. <releasetypes>extbin</releasetypes>
  73516. <releasetypes>zendextsrc</releasetypes>
  73517. <releasetypes>zendextbin</releasetypes>
  73518. <installable>1</installable>
  73519. <locationconfig>php_dir</locationconfig>
  73520. <honorsbaseinstall>1</honorsbaseinstall>
  73521. <unusualbaseinstall />
  73522. <phpfile>1</phpfile>
  73523. <executable />
  73524. <phpextension />
  73525. <config_vars />
  73526. </role><?php
  73527. /**
  73528. * PEAR_Installer_Role_Script
  73529. *
  73530. * PHP versions 4 and 5
  73531. *
  73532. * @category pear
  73533. * @package PEAR
  73534. * @author Greg Beaver <cellog@php.net>
  73535. * @copyright 1997-2009 The Authors
  73536. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  73537. * @link http://pear.php.net/package/PEAR
  73538. * @since File available since Release 1.4.0a1
  73539. */
  73540. /**
  73541. * @category pear
  73542. * @package PEAR
  73543. * @author Greg Beaver <cellog@php.net>
  73544. * @copyright 1997-2009 The Authors
  73545. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  73546. * @version Release: 1.10.16
  73547. * @link http://pear.php.net/package/PEAR
  73548. * @since Class available since Release 1.4.0a1
  73549. */
  73550. class PEAR_Installer_Role_Script extends PEAR_Installer_Role_Common {}
  73551. ?><role version="1.0">
  73552. <releasetypes>php</releasetypes>
  73553. <releasetypes>extsrc</releasetypes>
  73554. <releasetypes>extbin</releasetypes>
  73555. <releasetypes>zendextsrc</releasetypes>
  73556. <releasetypes>zendextbin</releasetypes>
  73557. <installable>1</installable>
  73558. <locationconfig>bin_dir</locationconfig>
  73559. <honorsbaseinstall>1</honorsbaseinstall>
  73560. <unusualbaseinstall />
  73561. <phpfile />
  73562. <executable>1</executable>
  73563. <phpextension />
  73564. <config_vars />
  73565. </role><?php
  73566. /**
  73567. * PEAR_Installer_Role_Test
  73568. *
  73569. * PHP versions 4 and 5
  73570. *
  73571. * @category pear
  73572. * @package PEAR
  73573. * @author Greg Beaver <cellog@php.net>
  73574. * @copyright 1997-2009 The Authors
  73575. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  73576. * @link http://pear.php.net/package/PEAR
  73577. * @since File available since Release 1.4.0a1
  73578. */
  73579. /**
  73580. * @category pear
  73581. * @package PEAR
  73582. * @author Greg Beaver <cellog@php.net>
  73583. * @copyright 1997-2009 The Authors
  73584. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  73585. * @version Release: 1.10.16
  73586. * @link http://pear.php.net/package/PEAR
  73587. * @since Class available since Release 1.4.0a1
  73588. */
  73589. class PEAR_Installer_Role_Test extends PEAR_Installer_Role_Common {}
  73590. ?><role version="1.0">
  73591. <releasetypes>php</releasetypes>
  73592. <releasetypes>extsrc</releasetypes>
  73593. <releasetypes>extbin</releasetypes>
  73594. <releasetypes>zendextsrc</releasetypes>
  73595. <releasetypes>zendextbin</releasetypes>
  73596. <installable>1</installable>
  73597. <locationconfig>test_dir</locationconfig>
  73598. <honorsbaseinstall />
  73599. <unusualbaseinstall />
  73600. <phpfile />
  73601. <executable />
  73602. <phpextension />
  73603. <config_vars />
  73604. </role><?php
  73605. /**
  73606. * PEAR_PackageFile, package.xml parsing utility class
  73607. *
  73608. * PHP versions 4 and 5
  73609. *
  73610. * @category pear
  73611. * @package PEAR
  73612. * @author Greg Beaver <cellog@php.net>
  73613. * @copyright 1997-2009 The Authors
  73614. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  73615. * @link http://pear.php.net/package/PEAR
  73616. * @since File available since Release 1.4.0a1
  73617. */
  73618. /**
  73619. * needed for PEAR_VALIDATE_* constants
  73620. */
  73621. require_once 'phar://go-pear.phar/' . 'PEAR/Validate.php';
  73622. /**
  73623. * Error code if the package.xml <package> tag does not contain a valid version
  73624. */
  73625. define('PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION', 1);
  73626. /**
  73627. * Error code if the package.xml <package> tag version is not supported (version 1.0 and 1.1 are the only supported versions,
  73628. * currently
  73629. */
  73630. define('PEAR_PACKAGEFILE_ERROR_INVALID_PACKAGEVERSION', 2);
  73631. /**
  73632. * Abstraction for the package.xml package description file
  73633. *
  73634. * @category pear
  73635. * @package PEAR
  73636. * @author Greg Beaver <cellog@php.net>
  73637. * @copyright 1997-2009 The Authors
  73638. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  73639. * @version Release: 1.10.16
  73640. * @link http://pear.php.net/package/PEAR
  73641. * @since Class available since Release 1.4.0a1
  73642. */
  73643. class PEAR_PackageFile
  73644. {
  73645. /**
  73646. * @var PEAR_Config
  73647. */
  73648. var $_config;
  73649. var $_debug;
  73650. var $_logger = false;
  73651. /**
  73652. * @var boolean
  73653. */
  73654. var $_rawReturn = false;
  73655. /**
  73656. * helper for extracting Archive_Tar errors
  73657. * @var array
  73658. * @access private
  73659. */
  73660. var $_extractErrors = array();
  73661. /**
  73662. *
  73663. * @param PEAR_Config $config
  73664. * @param ? $debug
  73665. * @param string @tmpdir Optional temporary directory for uncompressing
  73666. * files
  73667. */
  73668. function __construct(&$config, $debug = false)
  73669. {
  73670. $this->_config = $config;
  73671. $this->_debug = $debug;
  73672. }
  73673. /**
  73674. * Turn off validation - return a parsed package.xml without checking it
  73675. *
  73676. * This is used by the package-validate command
  73677. */
  73678. function rawReturn()
  73679. {
  73680. $this->_rawReturn = true;
  73681. }
  73682. function setLogger(&$l)
  73683. {
  73684. $this->_logger = &$l;
  73685. }
  73686. /**
  73687. * Create a PEAR_PackageFile_Parser_v* of a given version.
  73688. * @param int $version
  73689. * @return PEAR_PackageFile_Parser_v1|PEAR_PackageFile_Parser_v1
  73690. */
  73691. function &parserFactory($version)
  73692. {
  73693. if (!in_array($version[0], array('1', '2'))) {
  73694. $a = false;
  73695. return $a;
  73696. }
  73697. include_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/Parser/v' . $version[0] . '.php';
  73698. $version = $version[0];
  73699. $class = "PEAR_PackageFile_Parser_v$version";
  73700. $a = new $class;
  73701. return $a;
  73702. }
  73703. /**
  73704. * For simpler unit-testing
  73705. * @return string
  73706. */
  73707. function getClassPrefix()
  73708. {
  73709. return 'PEAR_PackageFile_v';
  73710. }
  73711. /**
  73712. * Create a PEAR_PackageFile_v* of a given version.
  73713. * @param int $version
  73714. * @return PEAR_PackageFile_v1|PEAR_PackageFile_v1
  73715. */
  73716. function &factory($version)
  73717. {
  73718. if (!in_array($version[0], array('1', '2'))) {
  73719. $a = false;
  73720. return $a;
  73721. }
  73722. include_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/v' . $version[0] . '.php';
  73723. $version = $version[0];
  73724. $class = $this->getClassPrefix() . $version;
  73725. $a = new $class;
  73726. return $a;
  73727. }
  73728. /**
  73729. * Create a PEAR_PackageFile_v* from its toArray() method
  73730. *
  73731. * WARNING: no validation is performed, the array is assumed to be valid,
  73732. * always parse from xml if you want validation.
  73733. * @param array $arr
  73734. * @return PEAR_PackageFileManager_v1|PEAR_PackageFileManager_v2
  73735. * @uses factory() to construct the returned object.
  73736. */
  73737. function &fromArray($arr)
  73738. {
  73739. if (isset($arr['xsdversion'])) {
  73740. $obj = &$this->factory($arr['xsdversion']);
  73741. if ($this->_logger) {
  73742. $obj->setLogger($this->_logger);
  73743. }
  73744. $obj->setConfig($this->_config);
  73745. $obj->fromArray($arr);
  73746. return $obj;
  73747. }
  73748. if (isset($arr['package']['attribs']['version'])) {
  73749. $obj = &$this->factory($arr['package']['attribs']['version']);
  73750. } else {
  73751. $obj = &$this->factory('1.0');
  73752. }
  73753. if ($this->_logger) {
  73754. $obj->setLogger($this->_logger);
  73755. }
  73756. $obj->setConfig($this->_config);
  73757. $obj->fromArray($arr);
  73758. return $obj;
  73759. }
  73760. /**
  73761. * Create a PEAR_PackageFile_v* from an XML string.
  73762. * @access public
  73763. * @param string $data contents of package.xml file
  73764. * @param int $state package state (one of PEAR_VALIDATE_* constants)
  73765. * @param string $file full path to the package.xml file (and the files
  73766. * it references)
  73767. * @param string $archive optional name of the archive that the XML was
  73768. * extracted from, if any
  73769. * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2
  73770. * @uses parserFactory() to construct a parser to load the package.
  73771. */
  73772. function &fromXmlString($data, $state, $file, $archive = false)
  73773. {
  73774. if (preg_match('/<package[^>]+version=[\'"]([0-9]+\.[0-9]+)[\'"]/', $data, $packageversion)) {
  73775. if (!in_array($packageversion[1], array('1.0', '2.0', '2.1'))) {
  73776. return PEAR::raiseError('package.xml version "' . $packageversion[1] .
  73777. '" is not supported, only 1.0, 2.0, and 2.1 are supported.');
  73778. }
  73779. $object = &$this->parserFactory($packageversion[1]);
  73780. if ($this->_logger) {
  73781. $object->setLogger($this->_logger);
  73782. }
  73783. $object->setConfig($this->_config);
  73784. $pf = $object->parse($data, $file, $archive);
  73785. if (PEAR::isError($pf)) {
  73786. return $pf;
  73787. }
  73788. if ($this->_rawReturn) {
  73789. return $pf;
  73790. }
  73791. if (!$pf->validate($state)) {;
  73792. if ($this->_config->get('verbose') > 0
  73793. && $this->_logger && $pf->getValidationWarnings(false)
  73794. ) {
  73795. foreach ($pf->getValidationWarnings(false) as $warning) {
  73796. $this->_logger->log(0, 'ERROR: ' . $warning['message']);
  73797. }
  73798. }
  73799. $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed',
  73800. 2, null, null, $pf->getValidationWarnings());
  73801. return $a;
  73802. }
  73803. if ($this->_logger && $pf->getValidationWarnings(false)) {
  73804. foreach ($pf->getValidationWarnings() as $warning) {
  73805. $this->_logger->log(0, 'WARNING: ' . $warning['message']);
  73806. }
  73807. }
  73808. if (method_exists($pf, 'flattenFilelist')) {
  73809. $pf->flattenFilelist(); // for v2
  73810. }
  73811. return $pf;
  73812. } elseif (preg_match('/<package[^>]+version=[\'"]([^"\']+)[\'"]/', $data, $packageversion)) {
  73813. $a = PEAR::raiseError('package.xml file "' . $file .
  73814. '" has unsupported package.xml <package> version "' . $packageversion[1] . '"');
  73815. return $a;
  73816. } else {
  73817. if (!class_exists('PEAR_ErrorStack')) {
  73818. require_once 'phar://go-pear.phar/' . 'PEAR/ErrorStack.php';
  73819. }
  73820. PEAR_ErrorStack::staticPush('PEAR_PackageFile',
  73821. PEAR_PACKAGEFILE_ERROR_NO_PACKAGEVERSION,
  73822. 'warning', array('xml' => $data), 'package.xml "' . $file .
  73823. '" has no package.xml <package> version');
  73824. $object = &$this->parserFactory('1.0');
  73825. $object->setConfig($this->_config);
  73826. $pf = $object->parse($data, $file, $archive);
  73827. if (PEAR::isError($pf)) {
  73828. return $pf;
  73829. }
  73830. if ($this->_rawReturn) {
  73831. return $pf;
  73832. }
  73833. if (!$pf->validate($state)) {
  73834. $a = PEAR::raiseError('Parsing of package.xml from file "' . $file . '" failed',
  73835. 2, null, null, $pf->getValidationWarnings());
  73836. return $a;
  73837. }
  73838. if ($this->_logger && $pf->getValidationWarnings(false)) {
  73839. foreach ($pf->getValidationWarnings() as $warning) {
  73840. $this->_logger->log(0, 'WARNING: ' . $warning['message']);
  73841. }
  73842. }
  73843. if (method_exists($pf, 'flattenFilelist')) {
  73844. $pf->flattenFilelist(); // for v2
  73845. }
  73846. return $pf;
  73847. }
  73848. }
  73849. /**
  73850. * Register a temporary file or directory. When the destructor is
  73851. * executed, all registered temporary files and directories are
  73852. * removed.
  73853. *
  73854. * @param string $file name of file or directory
  73855. * @return void
  73856. */
  73857. static function addTempFile($file)
  73858. {
  73859. $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
  73860. }
  73861. /**
  73862. * Create a PEAR_PackageFile_v* from a compressed Tar or Tgz file.
  73863. * @access public
  73864. * @param string contents of package.xml file
  73865. * @param int package state (one of PEAR_VALIDATE_* constants)
  73866. * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2
  73867. * @using Archive_Tar to extract the files
  73868. * @using fromPackageFile() to load the package after the package.xml
  73869. * file is extracted.
  73870. */
  73871. function &fromTgzFile($file, $state)
  73872. {
  73873. if (!class_exists('Archive_Tar')) {
  73874. require_once 'phar://go-pear.phar/' . 'Archive/Tar.php';
  73875. }
  73876. $tar = new Archive_Tar($file);
  73877. if ($this->_debug <= 1) {
  73878. $tar->pushErrorHandling(PEAR_ERROR_RETURN);
  73879. }
  73880. $content = $tar->listContent();
  73881. if ($this->_debug <= 1) {
  73882. $tar->popErrorHandling();
  73883. }
  73884. if (!is_array($content)) {
  73885. if (is_string($file) && strlen($file) < 255 &&
  73886. (!file_exists($file) || !@is_file($file))) {
  73887. $ret = PEAR::raiseError("could not open file \"$file\"");
  73888. return $ret;
  73889. }
  73890. $file = realpath($file);
  73891. $ret = PEAR::raiseError("Could not get contents of package \"$file\"".
  73892. '. Invalid tgz file.');
  73893. return $ret;
  73894. }
  73895. if (!count($content) && !@is_file($file)) {
  73896. $ret = PEAR::raiseError("could not open file \"$file\"");
  73897. return $ret;
  73898. }
  73899. $xml = null;
  73900. $origfile = $file;
  73901. foreach ($content as $file) {
  73902. $name = $file['filename'];
  73903. if ($name == 'package2.xml') { // allow a .tgz to distribute both versions
  73904. $xml = $name;
  73905. break;
  73906. }
  73907. if ($name == 'package.xml') {
  73908. $xml = $name;
  73909. break;
  73910. } elseif (preg_match('/package.xml$/', $name, $match)) {
  73911. $xml = $name;
  73912. break;
  73913. }
  73914. }
  73915. $tmpdir = System::mktemp('-t "' . $this->_config->get('temp_dir') . '" -d pear');
  73916. if ($tmpdir === false) {
  73917. $ret = PEAR::raiseError("there was a problem with getting the configured temp directory");
  73918. return $ret;
  73919. }
  73920. PEAR_PackageFile::addTempFile($tmpdir);
  73921. $this->_extractErrors();
  73922. PEAR::staticPushErrorHandling(PEAR_ERROR_CALLBACK, array($this, '_extractErrors'));
  73923. if (!$xml || !$tar->extractList(array($xml), $tmpdir)) {
  73924. $extra = implode("\n", $this->_extractErrors());
  73925. if ($extra) {
  73926. $extra = ' ' . $extra;
  73927. }
  73928. PEAR::staticPopErrorHandling();
  73929. $ret = PEAR::raiseError('could not extract the package.xml file from "' .
  73930. $origfile . '"' . $extra);
  73931. return $ret;
  73932. }
  73933. PEAR::staticPopErrorHandling();
  73934. $ret = &PEAR_PackageFile::fromPackageFile("$tmpdir/$xml", $state, $origfile);
  73935. return $ret;
  73936. }
  73937. /**
  73938. * helper callback for extracting Archive_Tar errors
  73939. *
  73940. * @param PEAR_Error|null $err
  73941. * @return array
  73942. * @access private
  73943. */
  73944. function _extractErrors($err = null)
  73945. {
  73946. static $errors = array();
  73947. if ($err === null) {
  73948. $e = $errors;
  73949. $errors = array();
  73950. return $e;
  73951. }
  73952. $errors[] = $err->getMessage();
  73953. }
  73954. /**
  73955. * Create a PEAR_PackageFile_v* from a package.xml file.
  73956. *
  73957. * @access public
  73958. * @param string $descfile name of package xml file
  73959. * @param int $state package state (one of PEAR_VALIDATE_* constants)
  73960. * @param string|false $archive name of the archive this package.xml came
  73961. * from, if any
  73962. * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2
  73963. * @uses PEAR_PackageFile::fromXmlString to create the oject after the
  73964. * XML is loaded from the package.xml file.
  73965. */
  73966. function &fromPackageFile($descfile, $state, $archive = false)
  73967. {
  73968. $fp = false;
  73969. if (is_string($descfile) && strlen($descfile) < 255 &&
  73970. (
  73971. !file_exists($descfile) || !is_file($descfile) || !is_readable($descfile)
  73972. || (!$fp = @fopen($descfile, 'r'))
  73973. )
  73974. ) {
  73975. $a = PEAR::raiseError("Unable to open $descfile");
  73976. return $a;
  73977. }
  73978. // read the whole thing so we only get one cdata callback
  73979. // for each block of cdata
  73980. fclose($fp);
  73981. $data = file_get_contents($descfile);
  73982. $ret = &PEAR_PackageFile::fromXmlString($data, $state, $descfile, $archive);
  73983. return $ret;
  73984. }
  73985. /**
  73986. * Create a PEAR_PackageFile_v* from a .tgz archive or package.xml file.
  73987. *
  73988. * This method is able to extract information about a package from a .tgz
  73989. * archive or from a XML package definition file.
  73990. *
  73991. * @access public
  73992. * @param string $info file name
  73993. * @param int $state package state (one of PEAR_VALIDATE_* constants)
  73994. * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2
  73995. * @uses fromPackageFile() if the file appears to be XML
  73996. * @uses fromTgzFile() to load all non-XML files
  73997. */
  73998. function &fromAnyFile($info, $state)
  73999. {
  74000. if (is_dir($info)) {
  74001. $dir_name = realpath($info);
  74002. if (file_exists($dir_name . '/package.xml')) {
  74003. $info = PEAR_PackageFile::fromPackageFile($dir_name . '/package.xml', $state);
  74004. } elseif (file_exists($dir_name . '/package2.xml')) {
  74005. $info = PEAR_PackageFile::fromPackageFile($dir_name . '/package2.xml', $state);
  74006. } else {
  74007. $info = PEAR::raiseError("No package definition found in '$info' directory");
  74008. }
  74009. return $info;
  74010. }
  74011. $fp = false;
  74012. if (is_string($info) && strlen($info) < 255 &&
  74013. (file_exists($info) || ($fp = @fopen($info, 'r')))
  74014. ) {
  74015. if ($fp) {
  74016. fclose($fp);
  74017. }
  74018. $tmp = substr($info, -4);
  74019. if ($tmp == '.xml') {
  74020. $info = &PEAR_PackageFile::fromPackageFile($info, $state);
  74021. } elseif ($tmp == '.tar' || $tmp == '.tgz') {
  74022. $info = &PEAR_PackageFile::fromTgzFile($info, $state);
  74023. } else {
  74024. $fp = fopen($info, 'r');
  74025. $test = fread($fp, 5);
  74026. fclose($fp);
  74027. if ($test == '<?xml') {
  74028. $info = &PEAR_PackageFile::fromPackageFile($info, $state);
  74029. } else {
  74030. $info = &PEAR_PackageFile::fromTgzFile($info, $state);
  74031. }
  74032. }
  74033. return $info;
  74034. }
  74035. $info = PEAR::raiseError("Cannot open '$info' for parsing");
  74036. return $info;
  74037. }
  74038. }
  74039. <?php
  74040. /**
  74041. * package.xml generation class, package.xml version 1.0
  74042. *
  74043. * PHP versions 4 and 5
  74044. *
  74045. * @category pear
  74046. * @package PEAR
  74047. * @author Greg Beaver <cellog@php.net>
  74048. * @copyright 1997-2009 The Authors
  74049. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  74050. * @link http://pear.php.net/package/PEAR
  74051. * @since File available since Release 1.4.0a1
  74052. */
  74053. /**
  74054. * needed for PEAR_VALIDATE_* constants
  74055. */
  74056. require_once 'phar://go-pear.phar/' . 'PEAR/Validate.php';
  74057. require_once 'phar://go-pear.phar/' . 'System.php';
  74058. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/v2.php';
  74059. /**
  74060. * This class converts a PEAR_PackageFile_v1 object into any output format.
  74061. *
  74062. * Supported output formats include array, XML string, and a PEAR_PackageFile_v2
  74063. * object, for converting package.xml 1.0 into package.xml 2.0 with no sweat.
  74064. * @category pear
  74065. * @package PEAR
  74066. * @author Greg Beaver <cellog@php.net>
  74067. * @copyright 1997-2009 The Authors
  74068. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  74069. * @version Release: 1.10.16
  74070. * @link http://pear.php.net/package/PEAR
  74071. * @since Class available since Release 1.4.0a1
  74072. */
  74073. class PEAR_PackageFile_Generator_v1
  74074. {
  74075. /**
  74076. * @var PEAR_PackageFile_v1
  74077. */
  74078. var $_packagefile;
  74079. function __construct(&$packagefile)
  74080. {
  74081. $this->_packagefile = &$packagefile;
  74082. }
  74083. function getPackagerVersion()
  74084. {
  74085. return '1.10.16';
  74086. }
  74087. /**
  74088. * @param PEAR_Packager
  74089. * @param bool if true, a .tgz is written, otherwise a .tar is written
  74090. * @param string|null directory in which to save the .tgz
  74091. * @return string|PEAR_Error location of package or error object
  74092. */
  74093. function toTgz(&$packager, $compress = true, $where = null)
  74094. {
  74095. require_once 'phar://go-pear.phar/' . 'Archive/Tar.php';
  74096. if ($where === null) {
  74097. if (!($where = System::mktemp(array('-d')))) {
  74098. return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: mktemp failed');
  74099. }
  74100. } elseif (!@System::mkDir(array('-p', $where))) {
  74101. return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: "' . $where . '" could' .
  74102. ' not be created');
  74103. }
  74104. if (file_exists($where . DIRECTORY_SEPARATOR . 'package.xml') &&
  74105. !is_file($where . DIRECTORY_SEPARATOR . 'package.xml')) {
  74106. return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: unable to save package.xml as' .
  74107. ' "' . $where . DIRECTORY_SEPARATOR . 'package.xml"');
  74108. }
  74109. if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) {
  74110. return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: invalid package file');
  74111. }
  74112. $pkginfo = $this->_packagefile->getArray();
  74113. $ext = $compress ? '.tgz' : '.tar';
  74114. $pkgver = $pkginfo['package'] . '-' . $pkginfo['version'];
  74115. $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
  74116. if (file_exists(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext) &&
  74117. !is_file(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext)) {
  74118. return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: cannot create tgz file "' .
  74119. getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext . '"');
  74120. }
  74121. if ($pkgfile = $this->_packagefile->getPackageFile()) {
  74122. $pkgdir = dirname(realpath($pkgfile));
  74123. $pkgfile = basename($pkgfile);
  74124. } else {
  74125. return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: package file object must ' .
  74126. 'be created from a real file');
  74127. }
  74128. // {{{ Create the package file list
  74129. $filelist = array();
  74130. $i = 0;
  74131. foreach ($this->_packagefile->getFilelist() as $fname => $atts) {
  74132. $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
  74133. if (!file_exists($file)) {
  74134. return PEAR::raiseError("File does not exist: $fname");
  74135. } else {
  74136. $filelist[$i++] = $file;
  74137. if (!isset($atts['md5sum'])) {
  74138. $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($file));
  74139. }
  74140. $packager->log(2, "Adding file $fname");
  74141. }
  74142. }
  74143. // }}}
  74144. $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true);
  74145. if ($packagexml) {
  74146. $tar = new Archive_Tar($dest_package, $compress);
  74147. $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
  74148. // ----- Creates with the package.xml file
  74149. $ok = $tar->createModify(array($packagexml), '', $where);
  74150. if (PEAR::isError($ok)) {
  74151. return $ok;
  74152. } elseif (!$ok) {
  74153. return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed');
  74154. }
  74155. // ----- Add the content of the package
  74156. if (!$tar->addModify($filelist, $pkgver, $pkgdir)) {
  74157. return PEAR::raiseError('PEAR_Packagefile_v1::toTgz: tarball creation failed');
  74158. }
  74159. return $dest_package;
  74160. }
  74161. }
  74162. /**
  74163. * @param string|null directory to place the package.xml in, or null for a temporary dir
  74164. * @param int one of the PEAR_VALIDATE_* constants
  74165. * @param string name of the generated file
  74166. * @param bool if true, then no analysis will be performed on role="php" files
  74167. * @return string|PEAR_Error path to the created file on success
  74168. */
  74169. function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml',
  74170. $nofilechecking = false)
  74171. {
  74172. if (!$this->_packagefile->validate($state, $nofilechecking)) {
  74173. return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: invalid package.xml',
  74174. null, null, null, $this->_packagefile->getValidationWarnings());
  74175. }
  74176. if ($where === null) {
  74177. if (!($where = System::mktemp(array('-d')))) {
  74178. return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: mktemp failed');
  74179. }
  74180. } elseif (!@System::mkDir(array('-p', $where))) {
  74181. return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: "' . $where . '" could' .
  74182. ' not be created');
  74183. }
  74184. $newpkgfile = $where . DIRECTORY_SEPARATOR . $name;
  74185. $np = @fopen($newpkgfile, 'wb');
  74186. if (!$np) {
  74187. return PEAR::raiseError('PEAR_Packagefile_v1::toPackageFile: unable to save ' .
  74188. "$name as $newpkgfile");
  74189. }
  74190. fwrite($np, $this->toXml($state, true));
  74191. fclose($np);
  74192. return $newpkgfile;
  74193. }
  74194. /**
  74195. * fix both XML encoding to be UTF8, and replace standard XML entities < > " & '
  74196. *
  74197. * @param string $string
  74198. * @return string
  74199. * @access private
  74200. */
  74201. function _fixXmlEncoding($string)
  74202. {
  74203. return strtr($string, array(
  74204. '&' => '&amp;',
  74205. '>' => '&gt;',
  74206. '<' => '&lt;',
  74207. '"' => '&quot;',
  74208. '\'' => '&apos;' ));
  74209. }
  74210. /**
  74211. * Return an XML document based on the package info (as returned
  74212. * by the PEAR_Common::infoFrom* methods).
  74213. *
  74214. * @return string XML data
  74215. */
  74216. function toXml($state = PEAR_VALIDATE_NORMAL, $nofilevalidation = false)
  74217. {
  74218. $this->_packagefile->setDate(date('Y-m-d'));
  74219. if (!$this->_packagefile->validate($state, $nofilevalidation)) {
  74220. return false;
  74221. }
  74222. $pkginfo = $this->_packagefile->getArray();
  74223. static $maint_map = array(
  74224. "handle" => "user",
  74225. "name" => "name",
  74226. "email" => "email",
  74227. "role" => "role",
  74228. );
  74229. $ret = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
  74230. $ret .= "<!DOCTYPE package SYSTEM \"http://pear.php.net/dtd/package-1.0\">\n";
  74231. $ret .= "<package version=\"1.0\" packagerversion=\"1.10.16\">\n" .
  74232. " <name>$pkginfo[package]</name>";
  74233. if (isset($pkginfo['extends'])) {
  74234. $ret .= "\n<extends>$pkginfo[extends]</extends>";
  74235. }
  74236. $ret .=
  74237. "\n <summary>".$this->_fixXmlEncoding($pkginfo['summary'])."</summary>\n" .
  74238. " <description>".trim($this->_fixXmlEncoding($pkginfo['description']))."\n </description>\n" .
  74239. " <maintainers>\n";
  74240. foreach ($pkginfo['maintainers'] as $maint) {
  74241. $ret .= " <maintainer>\n";
  74242. foreach ($maint_map as $idx => $elm) {
  74243. $ret .= " <$elm>";
  74244. $ret .= $this->_fixXmlEncoding($maint[$idx]);
  74245. $ret .= "</$elm>\n";
  74246. }
  74247. $ret .= " </maintainer>\n";
  74248. }
  74249. $ret .= " </maintainers>\n";
  74250. $ret .= $this->_makeReleaseXml($pkginfo, false, $state);
  74251. if (isset($pkginfo['changelog']) && count($pkginfo['changelog']) > 0) {
  74252. $ret .= " <changelog>\n";
  74253. foreach ($pkginfo['changelog'] as $oldrelease) {
  74254. $ret .= $this->_makeReleaseXml($oldrelease, true);
  74255. }
  74256. $ret .= " </changelog>\n";
  74257. }
  74258. $ret .= "</package>\n";
  74259. return $ret;
  74260. }
  74261. // }}}
  74262. // {{{ _makeReleaseXml()
  74263. /**
  74264. * Generate part of an XML description with release information.
  74265. *
  74266. * @param array $pkginfo array with release information
  74267. * @param bool $changelog whether the result will be in a changelog element
  74268. *
  74269. * @return string XML data
  74270. *
  74271. * @access private
  74272. */
  74273. function _makeReleaseXml($pkginfo, $changelog = false, $state = PEAR_VALIDATE_NORMAL)
  74274. {
  74275. // XXX QUOTE ENTITIES IN PCDATA, OR EMBED IN CDATA BLOCKS!!
  74276. $indent = $changelog ? " " : "";
  74277. $ret = "$indent <release>\n";
  74278. if (!empty($pkginfo['version'])) {
  74279. $ret .= "$indent <version>$pkginfo[version]</version>\n";
  74280. }
  74281. if (!empty($pkginfo['release_date'])) {
  74282. $ret .= "$indent <date>$pkginfo[release_date]</date>\n";
  74283. }
  74284. if (!empty($pkginfo['release_license'])) {
  74285. $ret .= "$indent <license>$pkginfo[release_license]</license>\n";
  74286. }
  74287. if (!empty($pkginfo['release_state'])) {
  74288. $ret .= "$indent <state>$pkginfo[release_state]</state>\n";
  74289. }
  74290. if (!empty($pkginfo['release_notes'])) {
  74291. $ret .= "$indent <notes>".trim($this->_fixXmlEncoding($pkginfo['release_notes']))
  74292. ."\n$indent </notes>\n";
  74293. }
  74294. if (!empty($pkginfo['release_warnings'])) {
  74295. $ret .= "$indent <warnings>".$this->_fixXmlEncoding($pkginfo['release_warnings'])."</warnings>\n";
  74296. }
  74297. if (isset($pkginfo['release_deps']) && sizeof($pkginfo['release_deps']) > 0) {
  74298. $ret .= "$indent <deps>\n";
  74299. foreach ($pkginfo['release_deps'] as $dep) {
  74300. $ret .= "$indent <dep type=\"$dep[type]\" rel=\"$dep[rel]\"";
  74301. if (isset($dep['version'])) {
  74302. $ret .= " version=\"$dep[version]\"";
  74303. }
  74304. if (isset($dep['optional'])) {
  74305. $ret .= " optional=\"$dep[optional]\"";
  74306. }
  74307. if (isset($dep['name'])) {
  74308. $ret .= ">$dep[name]</dep>\n";
  74309. } else {
  74310. $ret .= "/>\n";
  74311. }
  74312. }
  74313. $ret .= "$indent </deps>\n";
  74314. }
  74315. if (isset($pkginfo['configure_options'])) {
  74316. $ret .= "$indent <configureoptions>\n";
  74317. foreach ($pkginfo['configure_options'] as $c) {
  74318. $ret .= "$indent <configureoption name=\"".
  74319. $this->_fixXmlEncoding($c['name']) . "\"";
  74320. if (isset($c['default'])) {
  74321. $ret .= " default=\"" . $this->_fixXmlEncoding($c['default']) . "\"";
  74322. }
  74323. $ret .= " prompt=\"" . $this->_fixXmlEncoding($c['prompt']) . "\"";
  74324. $ret .= "/>\n";
  74325. }
  74326. $ret .= "$indent </configureoptions>\n";
  74327. }
  74328. if (isset($pkginfo['provides'])) {
  74329. foreach ($pkginfo['provides'] as $key => $what) {
  74330. $ret .= "$indent <provides type=\"$what[type]\" ";
  74331. $ret .= "name=\"$what[name]\" ";
  74332. if (isset($what['extends'])) {
  74333. $ret .= "extends=\"$what[extends]\" ";
  74334. }
  74335. $ret .= "/>\n";
  74336. }
  74337. }
  74338. if (isset($pkginfo['filelist'])) {
  74339. $ret .= "$indent <filelist>\n";
  74340. if ($state ^ PEAR_VALIDATE_PACKAGING) {
  74341. $ret .= $this->recursiveXmlFilelist($pkginfo['filelist']);
  74342. } else {
  74343. foreach ($pkginfo['filelist'] as $file => $fa) {
  74344. if (!isset($fa['role'])) {
  74345. $fa['role'] = '';
  74346. }
  74347. $ret .= "$indent <file role=\"$fa[role]\"";
  74348. if (isset($fa['baseinstalldir'])) {
  74349. $ret .= ' baseinstalldir="' .
  74350. $this->_fixXmlEncoding($fa['baseinstalldir']) . '"';
  74351. }
  74352. if (isset($fa['md5sum'])) {
  74353. $ret .= " md5sum=\"$fa[md5sum]\"";
  74354. }
  74355. if (isset($fa['platform'])) {
  74356. $ret .= " platform=\"$fa[platform]\"";
  74357. }
  74358. if (!empty($fa['install-as'])) {
  74359. $ret .= ' install-as="' .
  74360. $this->_fixXmlEncoding($fa['install-as']) . '"';
  74361. }
  74362. $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"';
  74363. if (empty($fa['replacements'])) {
  74364. $ret .= "/>\n";
  74365. } else {
  74366. $ret .= ">\n";
  74367. foreach ($fa['replacements'] as $r) {
  74368. $ret .= "$indent <replace";
  74369. foreach ($r as $k => $v) {
  74370. $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"';
  74371. }
  74372. $ret .= "/>\n";
  74373. }
  74374. $ret .= "$indent </file>\n";
  74375. }
  74376. }
  74377. }
  74378. $ret .= "$indent </filelist>\n";
  74379. }
  74380. $ret .= "$indent </release>\n";
  74381. return $ret;
  74382. }
  74383. /**
  74384. * @param array
  74385. * @access protected
  74386. */
  74387. function recursiveXmlFilelist($list)
  74388. {
  74389. $this->_dirs = array();
  74390. foreach ($list as $file => $attributes) {
  74391. $this->_addDir($this->_dirs, explode('/', dirname($file)), $file, $attributes);
  74392. }
  74393. return $this->_formatDir($this->_dirs);
  74394. }
  74395. /**
  74396. * @param array
  74397. * @param array
  74398. * @param string|null
  74399. * @param array|null
  74400. * @access private
  74401. */
  74402. function _addDir(&$dirs, $dir, $file = null, $attributes = null)
  74403. {
  74404. if ($dir == array() || $dir == array('.')) {
  74405. $dirs['files'][basename($file)] = $attributes;
  74406. return;
  74407. }
  74408. $curdir = array_shift($dir);
  74409. if (!isset($dirs['dirs'][$curdir])) {
  74410. $dirs['dirs'][$curdir] = array();
  74411. }
  74412. $this->_addDir($dirs['dirs'][$curdir], $dir, $file, $attributes);
  74413. }
  74414. /**
  74415. * @param array
  74416. * @param string
  74417. * @param string
  74418. * @access private
  74419. */
  74420. function _formatDir($dirs, $indent = '', $curdir = '')
  74421. {
  74422. $ret = '';
  74423. if (!count($dirs)) {
  74424. return '';
  74425. }
  74426. if (isset($dirs['dirs'])) {
  74427. uksort($dirs['dirs'], 'strnatcasecmp');
  74428. foreach ($dirs['dirs'] as $dir => $contents) {
  74429. $usedir = "$curdir/$dir";
  74430. $ret .= "$indent <dir name=\"$dir\">\n";
  74431. $ret .= $this->_formatDir($contents, "$indent ", $usedir);
  74432. $ret .= "$indent </dir> <!-- $usedir -->\n";
  74433. }
  74434. }
  74435. if (isset($dirs['files'])) {
  74436. uksort($dirs['files'], 'strnatcasecmp');
  74437. foreach ($dirs['files'] as $file => $attribs) {
  74438. $ret .= $this->_formatFile($file, $attribs, $indent);
  74439. }
  74440. }
  74441. return $ret;
  74442. }
  74443. /**
  74444. * @param string
  74445. * @param array
  74446. * @param string
  74447. * @access private
  74448. */
  74449. function _formatFile($file, $attributes, $indent)
  74450. {
  74451. $ret = "$indent <file role=\"$attributes[role]\"";
  74452. if (isset($attributes['baseinstalldir'])) {
  74453. $ret .= ' baseinstalldir="' .
  74454. $this->_fixXmlEncoding($attributes['baseinstalldir']) . '"';
  74455. }
  74456. if (isset($attributes['md5sum'])) {
  74457. $ret .= " md5sum=\"$attributes[md5sum]\"";
  74458. }
  74459. if (isset($attributes['platform'])) {
  74460. $ret .= " platform=\"$attributes[platform]\"";
  74461. }
  74462. if (!empty($attributes['install-as'])) {
  74463. $ret .= ' install-as="' .
  74464. $this->_fixXmlEncoding($attributes['install-as']) . '"';
  74465. }
  74466. $ret .= ' name="' . $this->_fixXmlEncoding($file) . '"';
  74467. if (empty($attributes['replacements'])) {
  74468. $ret .= "/>\n";
  74469. } else {
  74470. $ret .= ">\n";
  74471. foreach ($attributes['replacements'] as $r) {
  74472. $ret .= "$indent <replace";
  74473. foreach ($r as $k => $v) {
  74474. $ret .= " $k=\"" . $this->_fixXmlEncoding($v) .'"';
  74475. }
  74476. $ret .= "/>\n";
  74477. }
  74478. $ret .= "$indent </file>\n";
  74479. }
  74480. return $ret;
  74481. }
  74482. // {{{ _unIndent()
  74483. /**
  74484. * Unindent given string (?)
  74485. *
  74486. * @param string $str The string that has to be unindented.
  74487. * @return string
  74488. * @access private
  74489. */
  74490. function _unIndent($str)
  74491. {
  74492. // remove leading newlines
  74493. $str = preg_replace('/^[\r\n]+/', '', $str);
  74494. // find whitespace at the beginning of the first line
  74495. $indent_len = strspn($str, " \t");
  74496. $indent = substr($str, 0, $indent_len);
  74497. $data = '';
  74498. // remove the same amount of whitespace from following lines
  74499. foreach (explode("\n", $str) as $line) {
  74500. if (substr($line, 0, $indent_len) == $indent) {
  74501. $data .= substr($line, $indent_len) . "\n";
  74502. }
  74503. }
  74504. return $data;
  74505. }
  74506. /**
  74507. * @return array
  74508. */
  74509. function dependenciesToV2()
  74510. {
  74511. $arr = array();
  74512. $this->_convertDependencies2_0($arr);
  74513. return $arr['dependencies'];
  74514. }
  74515. /**
  74516. * Convert a package.xml version 1.0 into version 2.0
  74517. *
  74518. * Note that this does a basic conversion, to allow more advanced
  74519. * features like bundles and multiple releases
  74520. * @param string the classname to instantiate and return. This must be
  74521. * PEAR_PackageFile_v2 or a descendant
  74522. * @param boolean if true, only valid, deterministic package.xml 1.0 as defined by the
  74523. * strictest parameters will be converted
  74524. * @return PEAR_PackageFile_v2|PEAR_Error
  74525. */
  74526. function &toV2($class = 'PEAR_PackageFile_v2', $strict = false)
  74527. {
  74528. if ($strict) {
  74529. if (!$this->_packagefile->validate()) {
  74530. $a = PEAR::raiseError('invalid package.xml version 1.0 cannot be converted' .
  74531. ' to version 2.0', null, null, null,
  74532. $this->_packagefile->getValidationWarnings(true));
  74533. return $a;
  74534. }
  74535. }
  74536. $arr = array(
  74537. 'attribs' => array(
  74538. 'version' => '2.0',
  74539. 'xmlns' => 'http://pear.php.net/dtd/package-2.0',
  74540. 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
  74541. 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
  74542. 'xsi:schemaLocation' => "http://pear.php.net/dtd/tasks-1.0\n" .
  74543. "http://pear.php.net/dtd/tasks-1.0.xsd\n" .
  74544. "http://pear.php.net/dtd/package-2.0\n" .
  74545. 'http://pear.php.net/dtd/package-2.0.xsd',
  74546. ),
  74547. 'name' => $this->_packagefile->getPackage(),
  74548. 'channel' => 'pear.php.net',
  74549. );
  74550. $arr['summary'] = $this->_packagefile->getSummary();
  74551. $arr['description'] = $this->_packagefile->getDescription();
  74552. $maintainers = $this->_packagefile->getMaintainers();
  74553. foreach ($maintainers as $maintainer) {
  74554. if ($maintainer['role'] != 'lead') {
  74555. continue;
  74556. }
  74557. $new = array(
  74558. 'name' => $maintainer['name'],
  74559. 'user' => $maintainer['handle'],
  74560. 'email' => $maintainer['email'],
  74561. 'active' => 'yes',
  74562. );
  74563. $arr['lead'][] = $new;
  74564. }
  74565. if (!isset($arr['lead'])) { // some people... you know?
  74566. $arr['lead'] = array(
  74567. 'name' => 'unknown',
  74568. 'user' => 'unknown',
  74569. 'email' => 'noleadmaintainer@example.com',
  74570. 'active' => 'no',
  74571. );
  74572. }
  74573. if (count($arr['lead']) == 1) {
  74574. $arr['lead'] = $arr['lead'][0];
  74575. }
  74576. foreach ($maintainers as $maintainer) {
  74577. if ($maintainer['role'] == 'lead') {
  74578. continue;
  74579. }
  74580. $new = array(
  74581. 'name' => $maintainer['name'],
  74582. 'user' => $maintainer['handle'],
  74583. 'email' => $maintainer['email'],
  74584. 'active' => 'yes',
  74585. );
  74586. $arr[$maintainer['role']][] = $new;
  74587. }
  74588. if (isset($arr['developer']) && count($arr['developer']) == 1) {
  74589. $arr['developer'] = $arr['developer'][0];
  74590. }
  74591. if (isset($arr['contributor']) && count($arr['contributor']) == 1) {
  74592. $arr['contributor'] = $arr['contributor'][0];
  74593. }
  74594. if (isset($arr['helper']) && count($arr['helper']) == 1) {
  74595. $arr['helper'] = $arr['helper'][0];
  74596. }
  74597. $arr['date'] = $this->_packagefile->getDate();
  74598. $arr['version'] =
  74599. array(
  74600. 'release' => $this->_packagefile->getVersion(),
  74601. 'api' => $this->_packagefile->getVersion(),
  74602. );
  74603. $arr['stability'] =
  74604. array(
  74605. 'release' => $this->_packagefile->getState(),
  74606. 'api' => $this->_packagefile->getState(),
  74607. );
  74608. $licensemap =
  74609. array(
  74610. 'php' => 'http://www.php.net/license',
  74611. 'php license' => 'http://www.php.net/license',
  74612. 'lgpl' => 'http://www.gnu.org/copyleft/lesser.html',
  74613. 'bsd' => 'http://www.opensource.org/licenses/bsd-license.php',
  74614. 'bsd style' => 'http://www.opensource.org/licenses/bsd-license.php',
  74615. 'bsd-style' => 'http://www.opensource.org/licenses/bsd-license.php',
  74616. 'mit' => 'http://www.opensource.org/licenses/mit-license.php',
  74617. 'gpl' => 'http://www.gnu.org/copyleft/gpl.html',
  74618. 'apache' => 'http://www.opensource.org/licenses/apache2.0.php'
  74619. );
  74620. if (isset($licensemap[strtolower($this->_packagefile->getLicense())])) {
  74621. $arr['license'] = array(
  74622. 'attribs' => array('uri' =>
  74623. $licensemap[strtolower($this->_packagefile->getLicense())]),
  74624. '_content' => $this->_packagefile->getLicense()
  74625. );
  74626. } else {
  74627. // don't use bogus uri
  74628. $arr['license'] = $this->_packagefile->getLicense();
  74629. }
  74630. $arr['notes'] = $this->_packagefile->getNotes();
  74631. $temp = array();
  74632. $arr['contents'] = $this->_convertFilelist2_0($temp);
  74633. $this->_convertDependencies2_0($arr);
  74634. $release = ($this->_packagefile->getConfigureOptions() || $this->_isExtension) ?
  74635. 'extsrcrelease' : 'phprelease';
  74636. if ($release == 'extsrcrelease') {
  74637. $arr['channel'] = 'pecl.php.net';
  74638. $arr['providesextension'] = $arr['name']; // assumption
  74639. }
  74640. $arr[$release] = array();
  74641. if ($this->_packagefile->getConfigureOptions()) {
  74642. $arr[$release]['configureoption'] = $this->_packagefile->getConfigureOptions();
  74643. foreach ($arr[$release]['configureoption'] as $i => $opt) {
  74644. $arr[$release]['configureoption'][$i] = array('attribs' => $opt);
  74645. }
  74646. if (count($arr[$release]['configureoption']) == 1) {
  74647. $arr[$release]['configureoption'] = $arr[$release]['configureoption'][0];
  74648. }
  74649. }
  74650. $this->_convertRelease2_0($arr[$release], $temp);
  74651. if ($release == 'extsrcrelease' && count($arr[$release]) > 1) {
  74652. // multiple extsrcrelease tags added in PEAR 1.4.1
  74653. $arr['dependencies']['required']['pearinstaller']['min'] = '1.4.1';
  74654. }
  74655. if ($cl = $this->_packagefile->getChangelog()) {
  74656. foreach ($cl as $release) {
  74657. $rel = array();
  74658. $rel['version'] =
  74659. array(
  74660. 'release' => $release['version'],
  74661. 'api' => $release['version'],
  74662. );
  74663. if (!isset($release['release_state'])) {
  74664. $release['release_state'] = 'stable';
  74665. }
  74666. $rel['stability'] =
  74667. array(
  74668. 'release' => $release['release_state'],
  74669. 'api' => $release['release_state'],
  74670. );
  74671. if (isset($release['release_date'])) {
  74672. $rel['date'] = $release['release_date'];
  74673. } else {
  74674. $rel['date'] = date('Y-m-d');
  74675. }
  74676. if (isset($release['release_license'])) {
  74677. if (isset($licensemap[strtolower($release['release_license'])])) {
  74678. $uri = $licensemap[strtolower($release['release_license'])];
  74679. } else {
  74680. $uri = 'http://www.example.com';
  74681. }
  74682. $rel['license'] = array(
  74683. 'attribs' => array('uri' => $uri),
  74684. '_content' => $release['release_license']
  74685. );
  74686. } else {
  74687. $rel['license'] = $arr['license'];
  74688. }
  74689. if (!isset($release['release_notes'])) {
  74690. $release['release_notes'] = 'no release notes';
  74691. }
  74692. $rel['notes'] = $release['release_notes'];
  74693. $arr['changelog']['release'][] = $rel;
  74694. }
  74695. }
  74696. $ret = new $class;
  74697. $ret->setConfig($this->_packagefile->_config);
  74698. if (isset($this->_packagefile->_logger) && is_object($this->_packagefile->_logger)) {
  74699. $ret->setLogger($this->_packagefile->_logger);
  74700. }
  74701. $ret->fromArray($arr);
  74702. return $ret;
  74703. }
  74704. /**
  74705. * @param array
  74706. * @param bool
  74707. * @access private
  74708. */
  74709. function _convertDependencies2_0(&$release, $internal = false)
  74710. {
  74711. $peardep = array('pearinstaller' =>
  74712. array('min' => '1.4.0b1')); // this is a lot safer
  74713. $required = $optional = array();
  74714. $release['dependencies'] = array('required' => array());
  74715. if ($this->_packagefile->hasDeps()) {
  74716. foreach ($this->_packagefile->getDeps() as $dep) {
  74717. if (!isset($dep['optional']) || $dep['optional'] == 'no') {
  74718. $required[] = $dep;
  74719. } else {
  74720. $optional[] = $dep;
  74721. }
  74722. }
  74723. foreach (array('required', 'optional') as $arr) {
  74724. $deps = array();
  74725. foreach ($$arr as $dep) {
  74726. // organize deps by dependency type and name
  74727. if (!isset($deps[$dep['type']])) {
  74728. $deps[$dep['type']] = array();
  74729. }
  74730. if (isset($dep['name'])) {
  74731. $deps[$dep['type']][$dep['name']][] = $dep;
  74732. } else {
  74733. $deps[$dep['type']][] = $dep;
  74734. }
  74735. }
  74736. do {
  74737. if (isset($deps['php'])) {
  74738. $php = array();
  74739. if (count($deps['php']) > 1) {
  74740. $php = $this->_processPhpDeps($deps['php']);
  74741. } else {
  74742. if (!isset($deps['php'][0])) {
  74743. // Buggy versions
  74744. $key = key($deps['php']);
  74745. $info = current($deps['php']);
  74746. $deps['php'] = array($info[0]);
  74747. }
  74748. $php = $this->_processDep($deps['php'][0]);
  74749. if (!$php) {
  74750. break; // poor mans throw
  74751. }
  74752. }
  74753. $release['dependencies'][$arr]['php'] = $php;
  74754. }
  74755. } while (false);
  74756. do {
  74757. if (isset($deps['pkg'])) {
  74758. $pkg = array();
  74759. $pkg = $this->_processMultipleDepsName($deps['pkg']);
  74760. if (!$pkg) {
  74761. break; // poor mans throw
  74762. }
  74763. $release['dependencies'][$arr]['package'] = $pkg;
  74764. }
  74765. } while (false);
  74766. do {
  74767. if (isset($deps['ext'])) {
  74768. $pkg = array();
  74769. $pkg = $this->_processMultipleDepsName($deps['ext']);
  74770. $release['dependencies'][$arr]['extension'] = $pkg;
  74771. }
  74772. } while (false);
  74773. // skip sapi - it's not supported so nobody will have used it
  74774. // skip os - it's not supported in 1.0
  74775. }
  74776. }
  74777. if (isset($release['dependencies']['required'])) {
  74778. $release['dependencies']['required'] =
  74779. array_merge($peardep, $release['dependencies']['required']);
  74780. } else {
  74781. $release['dependencies']['required'] = $peardep;
  74782. }
  74783. if (!isset($release['dependencies']['required']['php'])) {
  74784. $release['dependencies']['required']['php'] =
  74785. array('min' => '4.0.0');
  74786. }
  74787. $order = array();
  74788. $bewm = $release['dependencies']['required'];
  74789. $order['php'] = $bewm['php'];
  74790. $order['pearinstaller'] = $bewm['pearinstaller'];
  74791. isset($bewm['package']) ? $order['package'] = $bewm['package'] :0;
  74792. isset($bewm['extension']) ? $order['extension'] = $bewm['extension'] :0;
  74793. $release['dependencies']['required'] = $order;
  74794. }
  74795. /**
  74796. * @param array
  74797. * @access private
  74798. */
  74799. function _convertFilelist2_0(&$package)
  74800. {
  74801. $ret = array('dir' =>
  74802. array(
  74803. 'attribs' => array('name' => '/'),
  74804. 'file' => array()
  74805. )
  74806. );
  74807. $package['platform'] =
  74808. $package['install-as'] = array();
  74809. $this->_isExtension = false;
  74810. foreach ($this->_packagefile->getFilelist() as $name => $file) {
  74811. $file['name'] = $name;
  74812. if (isset($file['role']) && $file['role'] == 'src') {
  74813. $this->_isExtension = true;
  74814. }
  74815. if (isset($file['replacements'])) {
  74816. $repl = $file['replacements'];
  74817. unset($file['replacements']);
  74818. } else {
  74819. unset($repl);
  74820. }
  74821. if (isset($file['install-as'])) {
  74822. $package['install-as'][$name] = $file['install-as'];
  74823. unset($file['install-as']);
  74824. }
  74825. if (isset($file['platform'])) {
  74826. $package['platform'][$name] = $file['platform'];
  74827. unset($file['platform']);
  74828. }
  74829. $file = array('attribs' => $file);
  74830. if (isset($repl)) {
  74831. foreach ($repl as $replace ) {
  74832. $file['tasks:replace'][] = array('attribs' => $replace);
  74833. }
  74834. if (count($repl) == 1) {
  74835. $file['tasks:replace'] = $file['tasks:replace'][0];
  74836. }
  74837. }
  74838. $ret['dir']['file'][] = $file;
  74839. }
  74840. return $ret;
  74841. }
  74842. /**
  74843. * Post-process special files with install-as/platform attributes and
  74844. * make the release tag.
  74845. *
  74846. * This complex method follows this work-flow to create the release tags:
  74847. *
  74848. * <pre>
  74849. * - if any install-as/platform exist, create a generic release and fill it with
  74850. * o <install as=..> tags for <file name=... install-as=...>
  74851. * o <install as=..> tags for <file name=... platform=!... install-as=..>
  74852. * o <ignore> tags for <file name=... platform=...>
  74853. * o <ignore> tags for <file name=... platform=... install-as=..>
  74854. * - create a release for each platform encountered and fill with
  74855. * o <install as..> tags for <file name=... install-as=...>
  74856. * o <install as..> tags for <file name=... platform=this platform install-as=..>
  74857. * o <install as..> tags for <file name=... platform=!other platform install-as=..>
  74858. * o <ignore> tags for <file name=... platform=!this platform>
  74859. * o <ignore> tags for <file name=... platform=other platform>
  74860. * o <ignore> tags for <file name=... platform=other platform install-as=..>
  74861. * o <ignore> tags for <file name=... platform=!this platform install-as=..>
  74862. * </pre>
  74863. *
  74864. * It does this by accessing the $package parameter, which contains an array with
  74865. * indices:
  74866. *
  74867. * - platform: mapping of file => OS the file should be installed on
  74868. * - install-as: mapping of file => installed name
  74869. * - osmap: mapping of OS => list of files that should be installed
  74870. * on that OS
  74871. * - notosmap: mapping of OS => list of files that should not be
  74872. * installed on that OS
  74873. *
  74874. * @param array
  74875. * @param array
  74876. * @access private
  74877. */
  74878. function _convertRelease2_0(&$release, $package)
  74879. {
  74880. //- if any install-as/platform exist, create a generic release and fill it with
  74881. if (count($package['platform']) || count($package['install-as'])) {
  74882. $generic = array();
  74883. $genericIgnore = array();
  74884. foreach ($package['install-as'] as $file => $as) {
  74885. //o <install as=..> tags for <file name=... install-as=...>
  74886. if (!isset($package['platform'][$file])) {
  74887. $generic[] = $file;
  74888. continue;
  74889. }
  74890. //o <install as=..> tags for <file name=... platform=!... install-as=..>
  74891. if (isset($package['platform'][$file]) &&
  74892. $package['platform'][$file][0] == '!') {
  74893. $generic[] = $file;
  74894. continue;
  74895. }
  74896. //o <ignore> tags for <file name=... platform=... install-as=..>
  74897. if (isset($package['platform'][$file]) &&
  74898. $package['platform'][$file][0] != '!') {
  74899. $genericIgnore[] = $file;
  74900. continue;
  74901. }
  74902. }
  74903. foreach ($package['platform'] as $file => $platform) {
  74904. if (isset($package['install-as'][$file])) {
  74905. continue;
  74906. }
  74907. if ($platform[0] != '!') {
  74908. //o <ignore> tags for <file name=... platform=...>
  74909. $genericIgnore[] = $file;
  74910. }
  74911. }
  74912. if (count($package['platform'])) {
  74913. $oses = $notplatform = $platform = array();
  74914. foreach ($package['platform'] as $file => $os) {
  74915. // get a list of oses
  74916. if ($os[0] == '!') {
  74917. if (isset($oses[substr($os, 1)])) {
  74918. continue;
  74919. }
  74920. $oses[substr($os, 1)] = count($oses);
  74921. } else {
  74922. if (isset($oses[$os])) {
  74923. continue;
  74924. }
  74925. $oses[$os] = count($oses);
  74926. }
  74927. }
  74928. //- create a release for each platform encountered and fill with
  74929. foreach ($oses as $os => $releaseNum) {
  74930. $release[$releaseNum]['installconditions']['os']['name'] = $os;
  74931. $release[$releaseNum]['filelist'] = array('install' => array(),
  74932. 'ignore' => array());
  74933. foreach ($package['install-as'] as $file => $as) {
  74934. //o <install as=..> tags for <file name=... install-as=...>
  74935. if (!isset($package['platform'][$file])) {
  74936. $release[$releaseNum]['filelist']['install'][] =
  74937. array(
  74938. 'attribs' => array(
  74939. 'name' => $file,
  74940. 'as' => $as,
  74941. ),
  74942. );
  74943. continue;
  74944. }
  74945. //o <install as..> tags for
  74946. // <file name=... platform=this platform install-as=..>
  74947. if (isset($package['platform'][$file]) &&
  74948. $package['platform'][$file] == $os) {
  74949. $release[$releaseNum]['filelist']['install'][] =
  74950. array(
  74951. 'attribs' => array(
  74952. 'name' => $file,
  74953. 'as' => $as,
  74954. ),
  74955. );
  74956. continue;
  74957. }
  74958. //o <install as..> tags for
  74959. // <file name=... platform=!other platform install-as=..>
  74960. if (isset($package['platform'][$file]) &&
  74961. $package['platform'][$file] != "!$os" &&
  74962. $package['platform'][$file][0] == '!') {
  74963. $release[$releaseNum]['filelist']['install'][] =
  74964. array(
  74965. 'attribs' => array(
  74966. 'name' => $file,
  74967. 'as' => $as,
  74968. ),
  74969. );
  74970. continue;
  74971. }
  74972. //o <ignore> tags for
  74973. // <file name=... platform=!this platform install-as=..>
  74974. if (isset($package['platform'][$file]) &&
  74975. $package['platform'][$file] == "!$os") {
  74976. $release[$releaseNum]['filelist']['ignore'][] =
  74977. array(
  74978. 'attribs' => array(
  74979. 'name' => $file,
  74980. ),
  74981. );
  74982. continue;
  74983. }
  74984. //o <ignore> tags for
  74985. // <file name=... platform=other platform install-as=..>
  74986. if (isset($package['platform'][$file]) &&
  74987. $package['platform'][$file][0] != '!' &&
  74988. $package['platform'][$file] != $os) {
  74989. $release[$releaseNum]['filelist']['ignore'][] =
  74990. array(
  74991. 'attribs' => array(
  74992. 'name' => $file,
  74993. ),
  74994. );
  74995. continue;
  74996. }
  74997. }
  74998. foreach ($package['platform'] as $file => $platform) {
  74999. if (isset($package['install-as'][$file])) {
  75000. continue;
  75001. }
  75002. //o <ignore> tags for <file name=... platform=!this platform>
  75003. if ($platform == "!$os") {
  75004. $release[$releaseNum]['filelist']['ignore'][] =
  75005. array(
  75006. 'attribs' => array(
  75007. 'name' => $file,
  75008. ),
  75009. );
  75010. continue;
  75011. }
  75012. //o <ignore> tags for <file name=... platform=other platform>
  75013. if ($platform[0] != '!' && $platform != $os) {
  75014. $release[$releaseNum]['filelist']['ignore'][] =
  75015. array(
  75016. 'attribs' => array(
  75017. 'name' => $file,
  75018. ),
  75019. );
  75020. }
  75021. }
  75022. if (!count($release[$releaseNum]['filelist']['install'])) {
  75023. unset($release[$releaseNum]['filelist']['install']);
  75024. }
  75025. if (!count($release[$releaseNum]['filelist']['ignore'])) {
  75026. unset($release[$releaseNum]['filelist']['ignore']);
  75027. }
  75028. }
  75029. if (count($generic) || count($genericIgnore)) {
  75030. $release[count($oses)] = array();
  75031. if (count($generic)) {
  75032. foreach ($generic as $file) {
  75033. if (isset($package['install-as'][$file])) {
  75034. $installas = $package['install-as'][$file];
  75035. } else {
  75036. $installas = $file;
  75037. }
  75038. $release[count($oses)]['filelist']['install'][] =
  75039. array(
  75040. 'attribs' => array(
  75041. 'name' => $file,
  75042. 'as' => $installas,
  75043. )
  75044. );
  75045. }
  75046. }
  75047. if (count($genericIgnore)) {
  75048. foreach ($genericIgnore as $file) {
  75049. $release[count($oses)]['filelist']['ignore'][] =
  75050. array(
  75051. 'attribs' => array(
  75052. 'name' => $file,
  75053. )
  75054. );
  75055. }
  75056. }
  75057. }
  75058. // cleanup
  75059. foreach ($release as $i => $rel) {
  75060. if (isset($rel['filelist']['install']) &&
  75061. count($rel['filelist']['install']) == 1) {
  75062. $release[$i]['filelist']['install'] =
  75063. $release[$i]['filelist']['install'][0];
  75064. }
  75065. if (isset($rel['filelist']['ignore']) &&
  75066. count($rel['filelist']['ignore']) == 1) {
  75067. $release[$i]['filelist']['ignore'] =
  75068. $release[$i]['filelist']['ignore'][0];
  75069. }
  75070. }
  75071. if (count($release) == 1) {
  75072. $release = $release[0];
  75073. }
  75074. } else {
  75075. // no platform atts, but some install-as atts
  75076. foreach ($package['install-as'] as $file => $value) {
  75077. $release['filelist']['install'][] =
  75078. array(
  75079. 'attribs' => array(
  75080. 'name' => $file,
  75081. 'as' => $value
  75082. )
  75083. );
  75084. }
  75085. if (count($release['filelist']['install']) == 1) {
  75086. $release['filelist']['install'] = $release['filelist']['install'][0];
  75087. }
  75088. }
  75089. }
  75090. }
  75091. /**
  75092. * @param array
  75093. * @return array
  75094. * @access private
  75095. */
  75096. function _processDep($dep)
  75097. {
  75098. if ($dep['type'] == 'php') {
  75099. if ($dep['rel'] == 'has') {
  75100. // come on - everyone has php!
  75101. return false;
  75102. }
  75103. }
  75104. $php = array();
  75105. if ($dep['type'] != 'php') {
  75106. $php['name'] = $dep['name'];
  75107. if ($dep['type'] == 'pkg') {
  75108. $php['channel'] = 'pear.php.net';
  75109. }
  75110. }
  75111. switch ($dep['rel']) {
  75112. case 'gt' :
  75113. $php['min'] = $dep['version'];
  75114. $php['exclude'] = $dep['version'];
  75115. break;
  75116. case 'ge' :
  75117. if (!isset($dep['version'])) {
  75118. if ($dep['type'] == 'php') {
  75119. if (isset($dep['name'])) {
  75120. $dep['version'] = $dep['name'];
  75121. }
  75122. }
  75123. }
  75124. $php['min'] = $dep['version'];
  75125. break;
  75126. case 'lt' :
  75127. $php['max'] = $dep['version'];
  75128. $php['exclude'] = $dep['version'];
  75129. break;
  75130. case 'le' :
  75131. $php['max'] = $dep['version'];
  75132. break;
  75133. case 'eq' :
  75134. $php['min'] = $dep['version'];
  75135. $php['max'] = $dep['version'];
  75136. break;
  75137. case 'ne' :
  75138. $php['exclude'] = $dep['version'];
  75139. break;
  75140. case 'not' :
  75141. $php['conflicts'] = 'yes';
  75142. break;
  75143. }
  75144. return $php;
  75145. }
  75146. /**
  75147. * @param array
  75148. * @return array
  75149. */
  75150. function _processPhpDeps($deps)
  75151. {
  75152. $test = array();
  75153. foreach ($deps as $dep) {
  75154. $test[] = $this->_processDep($dep);
  75155. }
  75156. $min = array();
  75157. $max = array();
  75158. foreach ($test as $dep) {
  75159. if (!$dep) {
  75160. continue;
  75161. }
  75162. if (isset($dep['min'])) {
  75163. $min[$dep['min']] = count($min);
  75164. }
  75165. if (isset($dep['max'])) {
  75166. $max[$dep['max']] = count($max);
  75167. }
  75168. }
  75169. if (count($min) > 0) {
  75170. uksort($min, 'version_compare');
  75171. }
  75172. if (count($max) > 0) {
  75173. uksort($max, 'version_compare');
  75174. }
  75175. if (count($min)) {
  75176. // get the highest minimum
  75177. $a = array_flip($min);
  75178. $min = array_pop($a);
  75179. } else {
  75180. $min = false;
  75181. }
  75182. if (count($max)) {
  75183. // get the lowest maximum
  75184. $a = array_flip($max);
  75185. $max = array_shift($a);
  75186. } else {
  75187. $max = false;
  75188. }
  75189. if ($min) {
  75190. $php['min'] = $min;
  75191. }
  75192. if ($max) {
  75193. $php['max'] = $max;
  75194. }
  75195. $exclude = array();
  75196. foreach ($test as $dep) {
  75197. if (!isset($dep['exclude'])) {
  75198. continue;
  75199. }
  75200. $exclude[] = $dep['exclude'];
  75201. }
  75202. if (count($exclude)) {
  75203. $php['exclude'] = $exclude;
  75204. }
  75205. return $php;
  75206. }
  75207. /**
  75208. * process multiple dependencies that have a name, like package deps
  75209. * @param array
  75210. * @return array
  75211. * @access private
  75212. */
  75213. function _processMultipleDepsName($deps)
  75214. {
  75215. $ret = $tests = array();
  75216. foreach ($deps as $name => $dep) {
  75217. foreach ($dep as $d) {
  75218. $tests[$name][] = $this->_processDep($d);
  75219. }
  75220. }
  75221. foreach ($tests as $name => $test) {
  75222. $max = $min = $php = array();
  75223. $php['name'] = $name;
  75224. foreach ($test as $dep) {
  75225. if (!$dep) {
  75226. continue;
  75227. }
  75228. if (isset($dep['channel'])) {
  75229. $php['channel'] = 'pear.php.net';
  75230. }
  75231. if (isset($dep['conflicts']) && $dep['conflicts'] == 'yes') {
  75232. $php['conflicts'] = 'yes';
  75233. }
  75234. if (isset($dep['min'])) {
  75235. $min[$dep['min']] = count($min);
  75236. }
  75237. if (isset($dep['max'])) {
  75238. $max[$dep['max']] = count($max);
  75239. }
  75240. }
  75241. if (count($min) > 0) {
  75242. uksort($min, 'version_compare');
  75243. }
  75244. if (count($max) > 0) {
  75245. uksort($max, 'version_compare');
  75246. }
  75247. if (count($min)) {
  75248. // get the highest minimum
  75249. $a = array_flip($min);
  75250. $min = array_pop($a);
  75251. } else {
  75252. $min = false;
  75253. }
  75254. if (count($max)) {
  75255. // get the lowest maximum
  75256. $a = array_flip($max);
  75257. $max = array_shift($a);
  75258. } else {
  75259. $max = false;
  75260. }
  75261. if ($min) {
  75262. $php['min'] = $min;
  75263. }
  75264. if ($max) {
  75265. $php['max'] = $max;
  75266. }
  75267. $exclude = array();
  75268. foreach ($test as $dep) {
  75269. if (!isset($dep['exclude'])) {
  75270. continue;
  75271. }
  75272. $exclude[] = $dep['exclude'];
  75273. }
  75274. if (count($exclude)) {
  75275. $php['exclude'] = $exclude;
  75276. }
  75277. $ret[] = $php;
  75278. }
  75279. return $ret;
  75280. }
  75281. }
  75282. ?>
  75283. <?php
  75284. /**
  75285. * package.xml generation class, package.xml version 2.0
  75286. *
  75287. * PHP versions 4 and 5
  75288. *
  75289. * @category pear
  75290. * @package PEAR
  75291. * @author Greg Beaver <cellog@php.net>
  75292. * @author Stephan Schmidt (original XML_Serializer code)
  75293. * @copyright 1997-2009 The Authors
  75294. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  75295. * @link http://pear.php.net/package/PEAR
  75296. * @since File available since Release 1.4.0a1
  75297. */
  75298. /**
  75299. * file/dir manipulation routines
  75300. */
  75301. require_once 'phar://go-pear.phar/' . 'System.php';
  75302. require_once 'phar://go-pear.phar/' . 'XML/Util.php';
  75303. /**
  75304. * This class converts a PEAR_PackageFile_v2 object into any output format.
  75305. *
  75306. * Supported output formats include array, XML string (using S. Schmidt's
  75307. * XML_Serializer, slightly customized)
  75308. * @category pear
  75309. * @package PEAR
  75310. * @author Greg Beaver <cellog@php.net>
  75311. * @author Stephan Schmidt (original XML_Serializer code)
  75312. * @copyright 1997-2009 The Authors
  75313. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  75314. * @version Release: 1.10.16
  75315. * @link http://pear.php.net/package/PEAR
  75316. * @since Class available since Release 1.4.0a1
  75317. */
  75318. class PEAR_PackageFile_Generator_v2
  75319. {
  75320. /**
  75321. * default options for the serialization
  75322. * @access private
  75323. * @var array $_defaultOptions
  75324. */
  75325. var $_defaultOptions = array(
  75326. 'indent' => ' ', // string used for indentation
  75327. 'linebreak' => "\n", // string used for newlines
  75328. 'typeHints' => false, // automatically add type hin attributes
  75329. 'addDecl' => true, // add an XML declaration
  75330. 'defaultTagName' => 'XML_Serializer_Tag', // tag used for indexed arrays or invalid names
  75331. 'classAsTagName' => false, // use classname for objects in indexed arrays
  75332. 'keyAttribute' => '_originalKey', // attribute where original key is stored
  75333. 'typeAttribute' => '_type', // attribute for type (only if typeHints => true)
  75334. 'classAttribute' => '_class', // attribute for class of objects (only if typeHints => true)
  75335. 'scalarAsAttributes' => false, // scalar values (strings, ints,..) will be serialized as attribute
  75336. 'prependAttributes' => '', // prepend string for attributes
  75337. 'indentAttributes' => false, // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column
  75338. 'mode' => 'simplexml', // use 'simplexml' to use parent name as tagname if transforming an indexed array
  75339. 'addDoctype' => false, // add a doctype declaration
  75340. 'doctype' => null, // supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()}
  75341. 'rootName' => 'package', // name of the root tag
  75342. 'rootAttributes' => array(
  75343. 'version' => '2.0',
  75344. 'xmlns' => 'http://pear.php.net/dtd/package-2.0',
  75345. 'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
  75346. 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
  75347. 'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
  75348. http://pear.php.net/dtd/tasks-1.0.xsd
  75349. http://pear.php.net/dtd/package-2.0
  75350. http://pear.php.net/dtd/package-2.0.xsd',
  75351. ), // attributes of the root tag
  75352. 'attributesArray' => 'attribs', // all values in this key will be treated as attributes
  75353. 'contentName' => '_content', // this value will be used directly as content, instead of creating a new tag, may only be used in conjunction with attributesArray
  75354. 'beautifyFilelist' => false,
  75355. 'encoding' => 'UTF-8',
  75356. );
  75357. /**
  75358. * options for the serialization
  75359. * @access private
  75360. * @var array $options
  75361. */
  75362. var $options = array();
  75363. /**
  75364. * current tag depth
  75365. * @var integer $_tagDepth
  75366. */
  75367. var $_tagDepth = 0;
  75368. /**
  75369. * serilialized representation of the data
  75370. * @var string $_serializedData
  75371. */
  75372. var $_serializedData = null;
  75373. /**
  75374. * @var PEAR_PackageFile_v2
  75375. */
  75376. var $_packagefile;
  75377. /**
  75378. * @param PEAR_PackageFile_v2
  75379. */
  75380. function __construct(&$packagefile)
  75381. {
  75382. $this->_packagefile = &$packagefile;
  75383. if (isset($this->_packagefile->encoding)) {
  75384. $this->_defaultOptions['encoding'] = $this->_packagefile->encoding;
  75385. }
  75386. }
  75387. /**
  75388. * @return string
  75389. */
  75390. function getPackagerVersion()
  75391. {
  75392. return '1.10.16';
  75393. }
  75394. /**
  75395. * @param PEAR_Packager
  75396. * @param bool generate a .tgz or a .tar
  75397. * @param string|null temporary directory to package in
  75398. */
  75399. function toTgz(&$packager, $compress = true, $where = null)
  75400. {
  75401. $a = null;
  75402. return $this->toTgz2($packager, $a, $compress, $where);
  75403. }
  75404. /**
  75405. * Package up both a package.xml and package2.xml for the same release
  75406. * @param PEAR_Packager
  75407. * @param PEAR_PackageFile_v1
  75408. * @param bool generate a .tgz or a .tar
  75409. * @param string|null temporary directory to package in
  75410. */
  75411. function toTgz2(&$packager, &$pf1, $compress = true, $where = null)
  75412. {
  75413. require_once 'phar://go-pear.phar/' . 'Archive/Tar.php';
  75414. if (!$this->_packagefile->isEquivalent($pf1)) {
  75415. return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' .
  75416. basename($pf1->getPackageFile()) .
  75417. '" is not equivalent to "' . basename($this->_packagefile->getPackageFile())
  75418. . '"');
  75419. }
  75420. if ($where === null) {
  75421. if (!($where = System::mktemp(array('-d')))) {
  75422. return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: mktemp failed');
  75423. }
  75424. } elseif (!@System::mkDir(array('-p', $where))) {
  75425. return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' . $where . '" could' .
  75426. ' not be created');
  75427. }
  75428. $file = $where . DIRECTORY_SEPARATOR . 'package.xml';
  75429. if (file_exists($file) && !is_file($file)) {
  75430. return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: unable to save package.xml as' .
  75431. ' "' . $file .'"');
  75432. }
  75433. if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) {
  75434. return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: invalid package.xml');
  75435. }
  75436. $ext = $compress ? '.tgz' : '.tar';
  75437. $pkgver = $this->_packagefile->getPackage() . '-' . $this->_packagefile->getVersion();
  75438. $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
  75439. if (file_exists($dest_package) && !is_file($dest_package)) {
  75440. return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: cannot create tgz file "' .
  75441. $dest_package . '"');
  75442. }
  75443. $pkgfile = $this->_packagefile->getPackageFile();
  75444. if (!$pkgfile) {
  75445. return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: package file object must ' .
  75446. 'be created from a real file');
  75447. }
  75448. $pkgdir = dirname(realpath($pkgfile));
  75449. $pkgfile = basename($pkgfile);
  75450. // {{{ Create the package file list
  75451. $filelist = array();
  75452. $i = 0;
  75453. $this->_packagefile->flattenFilelist();
  75454. $contents = $this->_packagefile->getContents();
  75455. if (isset($contents['bundledpackage'])) { // bundles of packages
  75456. $contents = $contents['bundledpackage'];
  75457. if (!isset($contents[0])) {
  75458. $contents = array($contents);
  75459. }
  75460. $packageDir = $where;
  75461. foreach ($contents as $i => $package) {
  75462. $fname = $package;
  75463. $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
  75464. if (!file_exists($file)) {
  75465. return $packager->raiseError("File does not exist: $fname");
  75466. }
  75467. $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname;
  75468. System::mkdir(array('-p', dirname($tfile)));
  75469. copy($file, $tfile);
  75470. $filelist[$i++] = $tfile;
  75471. $packager->log(2, "Adding package $fname");
  75472. }
  75473. } else { // normal packages
  75474. $contents = $contents['dir']['file'];
  75475. if (!isset($contents[0])) {
  75476. $contents = array($contents);
  75477. }
  75478. $packageDir = $where;
  75479. foreach ($contents as $i => $file) {
  75480. $fname = $file['attribs']['name'];
  75481. $atts = $file['attribs'];
  75482. $orig = $file;
  75483. $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
  75484. if (!file_exists($file)) {
  75485. return $packager->raiseError("File does not exist: $fname");
  75486. }
  75487. $origperms = fileperms($file);
  75488. $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname;
  75489. unset($orig['attribs']);
  75490. if (count($orig)) { // file with tasks
  75491. // run any package-time tasks
  75492. $contents = file_get_contents($file);
  75493. foreach ($orig as $tag => $raw) {
  75494. $tag = str_replace(
  75495. array($this->_packagefile->getTasksNs() . ':', '-'),
  75496. array('', '_'), $tag);
  75497. $task = "PEAR_Task_$tag";
  75498. $task = new $task($this->_packagefile->_config,
  75499. $this->_packagefile->_logger,
  75500. PEAR_TASK_PACKAGE);
  75501. $task->init($raw, $atts, null);
  75502. $res = $task->startSession($this->_packagefile, $contents, $tfile);
  75503. if (!$res) {
  75504. continue; // skip this task
  75505. }
  75506. if (PEAR::isError($res)) {
  75507. return $res;
  75508. }
  75509. $contents = $res; // save changes
  75510. System::mkdir(array('-p', dirname($tfile)));
  75511. $wp = fopen($tfile, "wb");
  75512. fwrite($wp, $contents);
  75513. fclose($wp);
  75514. }
  75515. }
  75516. if (!file_exists($tfile)) {
  75517. System::mkdir(array('-p', dirname($tfile)));
  75518. copy($file, $tfile);
  75519. }
  75520. chmod($tfile, $origperms);
  75521. $filelist[$i++] = $tfile;
  75522. $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($tfile), $i - 1);
  75523. $packager->log(2, "Adding file $fname");
  75524. }
  75525. }
  75526. // }}}
  75527. $name = $pf1 !== null ? 'package2.xml' : 'package.xml';
  75528. $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, $name);
  75529. if ($packagexml) {
  75530. $tar = new Archive_Tar($dest_package, $compress);
  75531. $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
  75532. // ----- Creates with the package.xml file
  75533. $ok = $tar->createModify(array($packagexml), '', $where);
  75534. if (PEAR::isError($ok)) {
  75535. return $packager->raiseError($ok);
  75536. } elseif (!$ok) {
  75537. return $packager->raiseError('PEAR_Packagefile_v2::toTgz(): adding ' . $name .
  75538. ' failed');
  75539. }
  75540. // ----- Add the content of the package
  75541. if (!$tar->addModify($filelist, $pkgver, $where)) {
  75542. return $packager->raiseError(
  75543. 'PEAR_Packagefile_v2::toTgz(): tarball creation failed');
  75544. }
  75545. // add the package.xml version 1.0
  75546. if ($pf1 !== null) {
  75547. $pfgen = &$pf1->getDefaultGenerator();
  75548. $packagexml1 = $pfgen->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true);
  75549. if (!$tar->addModify(array($packagexml1), '', $where)) {
  75550. return $packager->raiseError(
  75551. 'PEAR_Packagefile_v2::toTgz(): adding package.xml failed');
  75552. }
  75553. }
  75554. return $dest_package;
  75555. }
  75556. }
  75557. function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml')
  75558. {
  75559. if (!$this->_packagefile->validate($state)) {
  75560. return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: invalid package.xml',
  75561. null, null, null, $this->_packagefile->getValidationWarnings());
  75562. }
  75563. if ($where === null) {
  75564. if (!($where = System::mktemp(array('-d')))) {
  75565. return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: mktemp failed');
  75566. }
  75567. } elseif (!@System::mkDir(array('-p', $where))) {
  75568. return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: "' . $where . '" could' .
  75569. ' not be created');
  75570. }
  75571. $newpkgfile = $where . DIRECTORY_SEPARATOR . $name;
  75572. $np = @fopen($newpkgfile, 'wb');
  75573. if (!$np) {
  75574. return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: unable to save ' .
  75575. "$name as $newpkgfile");
  75576. }
  75577. fwrite($np, $this->toXml($state));
  75578. fclose($np);
  75579. return $newpkgfile;
  75580. }
  75581. function &toV2()
  75582. {
  75583. return $this->_packagefile;
  75584. }
  75585. /**
  75586. * Return an XML document based on the package info (as returned
  75587. * by the PEAR_Common::infoFrom* methods).
  75588. *
  75589. * @return string XML data
  75590. */
  75591. function toXml($state = PEAR_VALIDATE_NORMAL, $options = array())
  75592. {
  75593. $this->_packagefile->setDate(date('Y-m-d'));
  75594. $this->_packagefile->setTime(date('H:i:s'));
  75595. if (!$this->_packagefile->validate($state)) {
  75596. return false;
  75597. }
  75598. if (is_array($options)) {
  75599. $this->options = array_merge($this->_defaultOptions, $options);
  75600. } else {
  75601. $this->options = $this->_defaultOptions;
  75602. }
  75603. $arr = $this->_packagefile->getArray();
  75604. if (isset($arr['filelist'])) {
  75605. unset($arr['filelist']);
  75606. }
  75607. if (isset($arr['_lastversion'])) {
  75608. unset($arr['_lastversion']);
  75609. }
  75610. // Fix the notes a little bit
  75611. if (isset($arr['notes'])) {
  75612. // This trims out the indenting, needs fixing
  75613. $arr['notes'] = "\n" . trim($arr['notes']) . "\n";
  75614. }
  75615. if (isset($arr['changelog']) && !empty($arr['changelog'])) {
  75616. // Fix for inconsistency how the array is filled depending on the changelog release amount
  75617. if (!isset($arr['changelog']['release'][0])) {
  75618. $release = $arr['changelog']['release'];
  75619. unset($arr['changelog']['release']);
  75620. $arr['changelog']['release'] = array();
  75621. $arr['changelog']['release'][0] = $release;
  75622. }
  75623. foreach (array_keys($arr['changelog']['release']) as $key) {
  75624. $c =& $arr['changelog']['release'][$key];
  75625. if (isset($c['notes'])) {
  75626. // This trims out the indenting, needs fixing
  75627. $c['notes'] = "\n" . trim($c['notes']) . "\n";
  75628. }
  75629. }
  75630. }
  75631. if ($state ^ PEAR_VALIDATE_PACKAGING && !isset($arr['bundle'])) {
  75632. $use = $this->_recursiveXmlFilelist($arr['contents']['dir']['file']);
  75633. unset($arr['contents']['dir']['file']);
  75634. if (isset($use['dir'])) {
  75635. $arr['contents']['dir']['dir'] = $use['dir'];
  75636. }
  75637. if (isset($use['file'])) {
  75638. $arr['contents']['dir']['file'] = $use['file'];
  75639. }
  75640. $this->options['beautifyFilelist'] = true;
  75641. }
  75642. $arr['attribs']['packagerversion'] = '1.10.16';
  75643. if ($this->serialize($arr, $options)) {
  75644. return $this->_serializedData . "\n";
  75645. }
  75646. return false;
  75647. }
  75648. function _recursiveXmlFilelist($list)
  75649. {
  75650. $dirs = array();
  75651. if (isset($list['attribs'])) {
  75652. $file = $list['attribs']['name'];
  75653. unset($list['attribs']['name']);
  75654. $attributes = $list['attribs'];
  75655. $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes);
  75656. } else {
  75657. foreach ($list as $a) {
  75658. $file = $a['attribs']['name'];
  75659. $attributes = $a['attribs'];
  75660. unset($a['attribs']);
  75661. $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes, $a);
  75662. }
  75663. }
  75664. $this->_formatDir($dirs);
  75665. $this->_deFormat($dirs);
  75666. return $dirs;
  75667. }
  75668. function _addDir(&$dirs, $dir, $file = null, $attributes = null, $tasks = null)
  75669. {
  75670. if (!$tasks) {
  75671. $tasks = array();
  75672. }
  75673. if ($dir == array() || $dir == array('.')) {
  75674. $dirs['file'][basename($file)] = $tasks;
  75675. $attributes['name'] = basename($file);
  75676. $dirs['file'][basename($file)]['attribs'] = $attributes;
  75677. return;
  75678. }
  75679. $curdir = array_shift($dir);
  75680. if (!isset($dirs['dir'][$curdir])) {
  75681. $dirs['dir'][$curdir] = array();
  75682. }
  75683. $this->_addDir($dirs['dir'][$curdir], $dir, $file, $attributes, $tasks);
  75684. }
  75685. function _formatDir(&$dirs)
  75686. {
  75687. if (!count($dirs)) {
  75688. return array();
  75689. }
  75690. $newdirs = array();
  75691. if (isset($dirs['dir'])) {
  75692. $newdirs['dir'] = $dirs['dir'];
  75693. }
  75694. if (isset($dirs['file'])) {
  75695. $newdirs['file'] = $dirs['file'];
  75696. }
  75697. $dirs = $newdirs;
  75698. if (isset($dirs['dir'])) {
  75699. uksort($dirs['dir'], 'strnatcasecmp');
  75700. foreach ($dirs['dir'] as $dir => $contents) {
  75701. $this->_formatDir($dirs['dir'][$dir]);
  75702. }
  75703. }
  75704. if (isset($dirs['file'])) {
  75705. uksort($dirs['file'], 'strnatcasecmp');
  75706. };
  75707. }
  75708. function _deFormat(&$dirs)
  75709. {
  75710. if (!count($dirs)) {
  75711. return array();
  75712. }
  75713. $newdirs = array();
  75714. if (isset($dirs['dir'])) {
  75715. foreach ($dirs['dir'] as $dir => $contents) {
  75716. $newdir = array();
  75717. $newdir['attribs']['name'] = $dir;
  75718. $this->_deFormat($contents);
  75719. foreach ($contents as $tag => $val) {
  75720. $newdir[$tag] = $val;
  75721. }
  75722. $newdirs['dir'][] = $newdir;
  75723. }
  75724. if (count($newdirs['dir']) == 1) {
  75725. $newdirs['dir'] = $newdirs['dir'][0];
  75726. }
  75727. }
  75728. if (isset($dirs['file'])) {
  75729. foreach ($dirs['file'] as $name => $file) {
  75730. $newdirs['file'][] = $file;
  75731. }
  75732. if (count($newdirs['file']) == 1) {
  75733. $newdirs['file'] = $newdirs['file'][0];
  75734. }
  75735. }
  75736. $dirs = $newdirs;
  75737. }
  75738. /**
  75739. * reset all options to default options
  75740. *
  75741. * @access public
  75742. * @see setOption(), XML_Unserializer()
  75743. */
  75744. function resetOptions()
  75745. {
  75746. $this->options = $this->_defaultOptions;
  75747. }
  75748. /**
  75749. * set an option
  75750. *
  75751. * You can use this method if you do not want to set all options in the constructor
  75752. *
  75753. * @access public
  75754. * @see resetOption(), XML_Serializer()
  75755. */
  75756. function setOption($name, $value)
  75757. {
  75758. $this->options[$name] = $value;
  75759. }
  75760. /**
  75761. * sets several options at once
  75762. *
  75763. * You can use this method if you do not want to set all options in the constructor
  75764. *
  75765. * @access public
  75766. * @see resetOption(), XML_Unserializer(), setOption()
  75767. */
  75768. function setOptions($options)
  75769. {
  75770. $this->options = array_merge($this->options, $options);
  75771. }
  75772. /**
  75773. * serialize data
  75774. *
  75775. * @access public
  75776. * @param mixed $data data to serialize
  75777. * @return boolean true on success, pear error on failure
  75778. */
  75779. function serialize($data, $options = null)
  75780. {
  75781. // if options have been specified, use them instead
  75782. // of the previously defined ones
  75783. if (is_array($options)) {
  75784. $optionsBak = $this->options;
  75785. if (isset($options['overrideOptions']) && $options['overrideOptions'] == true) {
  75786. $this->options = array_merge($this->_defaultOptions, $options);
  75787. } else {
  75788. $this->options = array_merge($this->options, $options);
  75789. }
  75790. } else {
  75791. $optionsBak = null;
  75792. }
  75793. // start depth is zero
  75794. $this->_tagDepth = 0;
  75795. $this->_serializedData = '';
  75796. // serialize an array
  75797. if (is_array($data)) {
  75798. $tagName = isset($this->options['rootName']) ? $this->options['rootName'] : 'array';
  75799. $this->_serializedData .= $this->_serializeArray($data, $tagName, $this->options['rootAttributes']);
  75800. }
  75801. // add doctype declaration
  75802. if ($this->options['addDoctype'] === true) {
  75803. $this->_serializedData = XML_Util::getDoctypeDeclaration($tagName, $this->options['doctype'])
  75804. . $this->options['linebreak']
  75805. . $this->_serializedData;
  75806. }
  75807. // build xml declaration
  75808. if ($this->options['addDecl']) {
  75809. $atts = array();
  75810. $encoding = isset($this->options['encoding']) ? $this->options['encoding'] : null;
  75811. $this->_serializedData = XML_Util::getXMLDeclaration('1.0', $encoding)
  75812. . $this->options['linebreak']
  75813. . $this->_serializedData;
  75814. }
  75815. if ($optionsBak !== null) {
  75816. $this->options = $optionsBak;
  75817. }
  75818. return true;
  75819. }
  75820. /**
  75821. * get the result of the serialization
  75822. *
  75823. * @access public
  75824. * @return string serialized XML
  75825. */
  75826. function getSerializedData()
  75827. {
  75828. if ($this->_serializedData === null) {
  75829. return $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.', XML_SERIALIZER_ERROR_NO_SERIALIZATION);
  75830. }
  75831. return $this->_serializedData;
  75832. }
  75833. /**
  75834. * serialize any value
  75835. *
  75836. * This method checks for the type of the value and calls the appropriate method
  75837. *
  75838. * @access private
  75839. * @param mixed $value
  75840. * @param string $tagName
  75841. * @param array $attributes
  75842. * @return string
  75843. */
  75844. function _serializeValue($value, $tagName = null, $attributes = array())
  75845. {
  75846. if (is_array($value)) {
  75847. $xml = $this->_serializeArray($value, $tagName, $attributes);
  75848. } elseif (is_object($value)) {
  75849. $xml = $this->_serializeObject($value, $tagName);
  75850. } else {
  75851. $tag = array(
  75852. 'qname' => $tagName,
  75853. 'attributes' => $attributes,
  75854. 'content' => $value
  75855. );
  75856. $xml = $this->_createXMLTag($tag);
  75857. }
  75858. return $xml;
  75859. }
  75860. /**
  75861. * serialize an array
  75862. *
  75863. * @access private
  75864. * @param array $array array to serialize
  75865. * @param string $tagName name of the root tag
  75866. * @param array $attributes attributes for the root tag
  75867. * @return string $string serialized data
  75868. * @uses XML_Util::isValidName() to check, whether key has to be substituted
  75869. */
  75870. function _serializeArray(&$array, $tagName = null, $attributes = array())
  75871. {
  75872. $_content = null;
  75873. /**
  75874. * check for special attributes
  75875. */
  75876. if ($this->options['attributesArray'] !== null) {
  75877. if (isset($array[$this->options['attributesArray']])) {
  75878. $attributes = $array[$this->options['attributesArray']];
  75879. unset($array[$this->options['attributesArray']]);
  75880. }
  75881. /**
  75882. * check for special content
  75883. */
  75884. if ($this->options['contentName'] !== null) {
  75885. if (isset($array[$this->options['contentName']])) {
  75886. $_content = $array[$this->options['contentName']];
  75887. unset($array[$this->options['contentName']]);
  75888. }
  75889. }
  75890. }
  75891. /*
  75892. * if mode is set to simpleXML, check whether
  75893. * the array is associative or indexed
  75894. */
  75895. if (is_array($array) && $this->options['mode'] == 'simplexml') {
  75896. $indexed = true;
  75897. if (!count($array)) {
  75898. $indexed = false;
  75899. }
  75900. foreach ($array as $key => $val) {
  75901. if (!is_int($key)) {
  75902. $indexed = false;
  75903. break;
  75904. }
  75905. }
  75906. if ($indexed && $this->options['mode'] == 'simplexml') {
  75907. $string = '';
  75908. foreach ($array as $key => $val) {
  75909. if ($this->options['beautifyFilelist'] && $tagName == 'dir') {
  75910. if (!isset($this->_curdir)) {
  75911. $this->_curdir = '';
  75912. }
  75913. $savedir = $this->_curdir;
  75914. if (isset($val['attribs'])) {
  75915. if ($val['attribs']['name'] == '/') {
  75916. $this->_curdir = '/';
  75917. } else {
  75918. if ($this->_curdir == '/') {
  75919. $this->_curdir = '';
  75920. }
  75921. $this->_curdir .= '/' . $val['attribs']['name'];
  75922. }
  75923. }
  75924. }
  75925. $string .= $this->_serializeValue( $val, $tagName, $attributes);
  75926. if ($this->options['beautifyFilelist'] && $tagName == 'dir') {
  75927. $string .= ' <!-- ' . $this->_curdir . ' -->';
  75928. if (empty($savedir)) {
  75929. unset($this->_curdir);
  75930. } else {
  75931. $this->_curdir = $savedir;
  75932. }
  75933. }
  75934. $string .= $this->options['linebreak'];
  75935. // do indentation
  75936. if ($this->options['indent'] !== null && $this->_tagDepth > 0) {
  75937. $string .= str_repeat($this->options['indent'], $this->_tagDepth);
  75938. }
  75939. }
  75940. return rtrim($string);
  75941. }
  75942. }
  75943. if ($this->options['scalarAsAttributes'] === true) {
  75944. foreach ($array as $key => $value) {
  75945. if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
  75946. unset($array[$key]);
  75947. $attributes[$this->options['prependAttributes'].$key] = $value;
  75948. }
  75949. }
  75950. }
  75951. // check for empty array => create empty tag
  75952. if (empty($array)) {
  75953. $tag = array(
  75954. 'qname' => $tagName,
  75955. 'content' => $_content,
  75956. 'attributes' => $attributes
  75957. );
  75958. } else {
  75959. $this->_tagDepth++;
  75960. $tmp = $this->options['linebreak'];
  75961. foreach ($array as $key => $value) {
  75962. // do indentation
  75963. if ($this->options['indent'] !== null && $this->_tagDepth > 0) {
  75964. $tmp .= str_repeat($this->options['indent'], $this->_tagDepth);
  75965. }
  75966. // copy key
  75967. $origKey = $key;
  75968. // key cannot be used as tagname => use default tag
  75969. $valid = XML_Util::isValidName($key);
  75970. if (PEAR::isError($valid)) {
  75971. if ($this->options['classAsTagName'] && is_object($value)) {
  75972. $key = get_class($value);
  75973. } else {
  75974. $key = $this->options['defaultTagName'];
  75975. }
  75976. }
  75977. $atts = array();
  75978. if ($this->options['typeHints'] === true) {
  75979. $atts[$this->options['typeAttribute']] = gettype($value);
  75980. if ($key !== $origKey) {
  75981. $atts[$this->options['keyAttribute']] = (string)$origKey;
  75982. }
  75983. }
  75984. if ($this->options['beautifyFilelist'] && $key == 'dir') {
  75985. if (!isset($this->_curdir)) {
  75986. $this->_curdir = '';
  75987. }
  75988. $savedir = $this->_curdir;
  75989. if (isset($value['attribs'])) {
  75990. if ($value['attribs']['name'] == '/') {
  75991. $this->_curdir = '/';
  75992. } else {
  75993. $this->_curdir .= '/' . $value['attribs']['name'];
  75994. }
  75995. }
  75996. }
  75997. if (is_string($value) && $value && ($value[strlen($value) - 1] == "\n")) {
  75998. $value .= str_repeat($this->options['indent'], $this->_tagDepth);
  75999. }
  76000. $tmp .= $this->_createXMLTag(array(
  76001. 'qname' => $key,
  76002. 'attributes' => $atts,
  76003. 'content' => $value )
  76004. );
  76005. if ($this->options['beautifyFilelist'] && $key == 'dir') {
  76006. if (isset($value['attribs'])) {
  76007. $tmp .= ' <!-- ' . $this->_curdir . ' -->';
  76008. if (empty($savedir)) {
  76009. unset($this->_curdir);
  76010. } else {
  76011. $this->_curdir = $savedir;
  76012. }
  76013. }
  76014. }
  76015. $tmp .= $this->options['linebreak'];
  76016. }
  76017. $this->_tagDepth--;
  76018. if ($this->options['indent']!==null && $this->_tagDepth>0) {
  76019. $tmp .= str_repeat($this->options['indent'], $this->_tagDepth);
  76020. }
  76021. if (trim($tmp) === '') {
  76022. $tmp = null;
  76023. }
  76024. $tag = array(
  76025. 'qname' => $tagName,
  76026. 'content' => $tmp,
  76027. 'attributes' => $attributes
  76028. );
  76029. }
  76030. if ($this->options['typeHints'] === true) {
  76031. if (!isset($tag['attributes'][$this->options['typeAttribute']])) {
  76032. $tag['attributes'][$this->options['typeAttribute']] = 'array';
  76033. }
  76034. }
  76035. $string = $this->_createXMLTag($tag, false);
  76036. return $string;
  76037. }
  76038. /**
  76039. * create a tag from an array
  76040. * this method awaits an array in the following format
  76041. * array(
  76042. * 'qname' => $tagName,
  76043. * 'attributes' => array(),
  76044. * 'content' => $content, // optional
  76045. * 'namespace' => $namespace // optional
  76046. * 'namespaceUri' => $namespaceUri // optional
  76047. * )
  76048. *
  76049. * @access private
  76050. * @param array $tag tag definition
  76051. * @param boolean $replaceEntities whether to replace XML entities in content or not
  76052. * @return string $string XML tag
  76053. */
  76054. function _createXMLTag($tag, $replaceEntities = true)
  76055. {
  76056. if ($this->options['indentAttributes'] !== false) {
  76057. $multiline = true;
  76058. $indent = str_repeat($this->options['indent'], $this->_tagDepth);
  76059. if ($this->options['indentAttributes'] == '_auto') {
  76060. $indent .= str_repeat(' ', (strlen($tag['qname'])+2));
  76061. } else {
  76062. $indent .= $this->options['indentAttributes'];
  76063. }
  76064. } else {
  76065. $indent = $multiline = false;
  76066. }
  76067. if (is_array($tag['content'])) {
  76068. if (empty($tag['content'])) {
  76069. $tag['content'] = '';
  76070. }
  76071. } elseif(is_scalar($tag['content']) && (string)$tag['content'] == '') {
  76072. $tag['content'] = '';
  76073. }
  76074. if (is_scalar($tag['content']) || is_null($tag['content'])) {
  76075. if ($replaceEntities === true) {
  76076. $replaceEntities = XML_UTIL_ENTITIES_XML;
  76077. }
  76078. $tag = XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options['linebreak']);
  76079. } elseif (is_array($tag['content'])) {
  76080. $tag = $this->_serializeArray($tag['content'], $tag['qname'], $tag['attributes']);
  76081. } elseif (is_object($tag['content'])) {
  76082. $tag = $this->_serializeObject($tag['content'], $tag['qname'], $tag['attributes']);
  76083. } elseif (is_resource($tag['content'])) {
  76084. settype($tag['content'], 'string');
  76085. $tag = XML_Util::createTagFromArray($tag, $replaceEntities);
  76086. }
  76087. return $tag;
  76088. }
  76089. }
  76090. <?php
  76091. /**
  76092. * package.xml parsing class, package.xml version 1.0
  76093. *
  76094. * PHP versions 4 and 5
  76095. *
  76096. * @category pear
  76097. * @package PEAR
  76098. * @author Greg Beaver <cellog@php.net>
  76099. * @copyright 1997-2009 The Authors
  76100. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  76101. * @link http://pear.php.net/package/PEAR
  76102. * @since File available since Release 1.4.0a1
  76103. */
  76104. /**
  76105. * package.xml abstraction class
  76106. */
  76107. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/v1.php';
  76108. /**
  76109. * Parser for package.xml version 1.0
  76110. * @category pear
  76111. * @package PEAR
  76112. * @author Greg Beaver <cellog@php.net>
  76113. * @copyright 1997-2009 The Authors
  76114. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  76115. * @version Release: 1.10.16
  76116. * @link http://pear.php.net/package/PEAR
  76117. * @since Class available since Release 1.4.0a1
  76118. */
  76119. class PEAR_PackageFile_Parser_v1
  76120. {
  76121. var $_registry;
  76122. var $_config;
  76123. var $_logger;
  76124. /**
  76125. * BC hack to allow PEAR_Common::infoFromString() to sort of
  76126. * work with the version 2.0 format - there's no filelist though
  76127. * @param PEAR_PackageFile_v2
  76128. */
  76129. function fromV2($packagefile)
  76130. {
  76131. $info = $packagefile->getArray(true);
  76132. $ret = new PEAR_PackageFile_v1;
  76133. $ret->fromArray($info['old']);
  76134. }
  76135. function setConfig(&$c)
  76136. {
  76137. $this->_config = &$c;
  76138. $this->_registry = &$c->getRegistry();
  76139. }
  76140. function setLogger(&$l)
  76141. {
  76142. $this->_logger = &$l;
  76143. }
  76144. /**
  76145. * @param string contents of package.xml file, version 1.0
  76146. * @return bool success of parsing
  76147. */
  76148. function &parse($data, $file, $archive = false)
  76149. {
  76150. if (!extension_loaded('xml')) {
  76151. return PEAR::raiseError('Cannot create xml parser for parsing package.xml, no xml extension');
  76152. }
  76153. $xp = xml_parser_create();
  76154. if (!$xp) {
  76155. $a = &PEAR::raiseError('Cannot create xml parser for parsing package.xml');
  76156. return $a;
  76157. }
  76158. xml_set_object($xp, $this);
  76159. xml_set_element_handler($xp, '_element_start_1_0', '_element_end_1_0');
  76160. xml_set_character_data_handler($xp, '_pkginfo_cdata_1_0');
  76161. xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false);
  76162. $this->element_stack = array();
  76163. $this->_packageInfo = array('provides' => array());
  76164. $this->current_element = false;
  76165. unset($this->dir_install);
  76166. $this->_packageInfo['filelist'] = array();
  76167. $this->filelist =& $this->_packageInfo['filelist'];
  76168. $this->dir_names = array();
  76169. $this->in_changelog = false;
  76170. $this->d_i = 0;
  76171. $this->cdata = '';
  76172. $this->_isValid = true;
  76173. if (!xml_parse($xp, $data, 1)) {
  76174. $code = xml_get_error_code($xp);
  76175. $line = xml_get_current_line_number($xp);
  76176. xml_parser_free($xp);
  76177. $a = PEAR::raiseError(sprintf("XML error: %s at line %d",
  76178. $str = xml_error_string($code), $line), 2);
  76179. return $a;
  76180. }
  76181. xml_parser_free($xp);
  76182. $pf = new PEAR_PackageFile_v1;
  76183. $pf->setConfig($this->_config);
  76184. if (isset($this->_logger)) {
  76185. $pf->setLogger($this->_logger);
  76186. }
  76187. $pf->setPackagefile($file, $archive);
  76188. $pf->fromArray($this->_packageInfo);
  76189. return $pf;
  76190. }
  76191. // {{{ _unIndent()
  76192. /**
  76193. * Unindent given string
  76194. *
  76195. * @param string $str The string that has to be unindented.
  76196. * @return string
  76197. * @access private
  76198. */
  76199. function _unIndent($str)
  76200. {
  76201. // remove leading newlines
  76202. $str = preg_replace('/^[\r\n]+/', '', $str);
  76203. // find whitespace at the beginning of the first line
  76204. $indent_len = strspn($str, " \t");
  76205. $indent = substr($str, 0, $indent_len);
  76206. $data = '';
  76207. // remove the same amount of whitespace from following lines
  76208. foreach (explode("\n", $str) as $line) {
  76209. if (substr($line, 0, $indent_len) == $indent) {
  76210. $data .= substr($line, $indent_len) . "\n";
  76211. } elseif (trim(substr($line, 0, $indent_len))) {
  76212. $data .= ltrim($line);
  76213. }
  76214. }
  76215. return $data;
  76216. }
  76217. // Support for package DTD v1.0:
  76218. // {{{ _element_start_1_0()
  76219. /**
  76220. * XML parser callback for ending elements. Used for version 1.0
  76221. * packages.
  76222. *
  76223. * @param resource $xp XML parser resource
  76224. * @param string $name name of ending element
  76225. *
  76226. * @return void
  76227. *
  76228. * @access private
  76229. */
  76230. function _element_start_1_0($xp, $name, $attribs)
  76231. {
  76232. array_push($this->element_stack, $name);
  76233. $this->current_element = $name;
  76234. $spos = sizeof($this->element_stack) - 2;
  76235. $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : '';
  76236. $this->current_attributes = $attribs;
  76237. $this->cdata = '';
  76238. switch ($name) {
  76239. case 'dir':
  76240. if ($this->in_changelog) {
  76241. break;
  76242. }
  76243. if (array_key_exists('name', $attribs) && $attribs['name'] != '/') {
  76244. $attribs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
  76245. $attribs['name']);
  76246. if (strrpos($attribs['name'], '/') === strlen($attribs['name']) - 1) {
  76247. $attribs['name'] = substr($attribs['name'], 0,
  76248. strlen($attribs['name']) - 1);
  76249. }
  76250. if (strpos($attribs['name'], '/') === 0) {
  76251. $attribs['name'] = substr($attribs['name'], 1);
  76252. }
  76253. $this->dir_names[] = $attribs['name'];
  76254. }
  76255. if (isset($attribs['baseinstalldir'])) {
  76256. $this->dir_install = $attribs['baseinstalldir'];
  76257. }
  76258. if (isset($attribs['role'])) {
  76259. $this->dir_role = $attribs['role'];
  76260. }
  76261. break;
  76262. case 'file':
  76263. if ($this->in_changelog) {
  76264. break;
  76265. }
  76266. if (isset($attribs['name'])) {
  76267. $path = '';
  76268. if (count($this->dir_names)) {
  76269. foreach ($this->dir_names as $dir) {
  76270. $path .= $dir . '/';
  76271. }
  76272. }
  76273. $path .= preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
  76274. $attribs['name']);
  76275. unset($attribs['name']);
  76276. $this->current_path = $path;
  76277. $this->filelist[$path] = $attribs;
  76278. // Set the baseinstalldir only if the file don't have this attrib
  76279. if (!isset($this->filelist[$path]['baseinstalldir']) &&
  76280. isset($this->dir_install))
  76281. {
  76282. $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
  76283. }
  76284. // Set the Role
  76285. if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
  76286. $this->filelist[$path]['role'] = $this->dir_role;
  76287. }
  76288. }
  76289. break;
  76290. case 'replace':
  76291. if (!$this->in_changelog) {
  76292. $this->filelist[$this->current_path]['replacements'][] = $attribs;
  76293. }
  76294. break;
  76295. case 'maintainers':
  76296. $this->_packageInfo['maintainers'] = array();
  76297. $this->m_i = 0; // maintainers array index
  76298. break;
  76299. case 'maintainer':
  76300. // compatibility check
  76301. if (!isset($this->_packageInfo['maintainers'])) {
  76302. $this->_packageInfo['maintainers'] = array();
  76303. $this->m_i = 0;
  76304. }
  76305. $this->_packageInfo['maintainers'][$this->m_i] = array();
  76306. $this->current_maintainer =& $this->_packageInfo['maintainers'][$this->m_i];
  76307. break;
  76308. case 'changelog':
  76309. $this->_packageInfo['changelog'] = array();
  76310. $this->c_i = 0; // changelog array index
  76311. $this->in_changelog = true;
  76312. break;
  76313. case 'release':
  76314. if ($this->in_changelog) {
  76315. $this->_packageInfo['changelog'][$this->c_i] = array();
  76316. $this->current_release = &$this->_packageInfo['changelog'][$this->c_i];
  76317. } else {
  76318. $this->current_release = &$this->_packageInfo;
  76319. }
  76320. break;
  76321. case 'deps':
  76322. if (!$this->in_changelog) {
  76323. $this->_packageInfo['release_deps'] = array();
  76324. }
  76325. break;
  76326. case 'dep':
  76327. // dependencies array index
  76328. if (!$this->in_changelog) {
  76329. $this->d_i++;
  76330. isset($attribs['type']) ? ($attribs['type'] = strtolower($attribs['type'])) : false;
  76331. $this->_packageInfo['release_deps'][$this->d_i] = $attribs;
  76332. }
  76333. break;
  76334. case 'configureoptions':
  76335. if (!$this->in_changelog) {
  76336. $this->_packageInfo['configure_options'] = array();
  76337. }
  76338. break;
  76339. case 'configureoption':
  76340. if (!$this->in_changelog) {
  76341. $this->_packageInfo['configure_options'][] = $attribs;
  76342. }
  76343. break;
  76344. case 'provides':
  76345. if (empty($attribs['type']) || empty($attribs['name'])) {
  76346. break;
  76347. }
  76348. $attribs['explicit'] = true;
  76349. $this->_packageInfo['provides']["$attribs[type];$attribs[name]"] = $attribs;
  76350. break;
  76351. case 'package' :
  76352. if (isset($attribs['version'])) {
  76353. $this->_packageInfo['xsdversion'] = trim($attribs['version']);
  76354. } else {
  76355. $this->_packageInfo['xsdversion'] = '1.0';
  76356. }
  76357. if (isset($attribs['packagerversion'])) {
  76358. $this->_packageInfo['packagerversion'] = $attribs['packagerversion'];
  76359. }
  76360. break;
  76361. }
  76362. }
  76363. // }}}
  76364. // {{{ _element_end_1_0()
  76365. /**
  76366. * XML parser callback for ending elements. Used for version 1.0
  76367. * packages.
  76368. *
  76369. * @param resource $xp XML parser resource
  76370. * @param string $name name of ending element
  76371. *
  76372. * @return void
  76373. *
  76374. * @access private
  76375. */
  76376. function _element_end_1_0($xp, $name)
  76377. {
  76378. $data = trim($this->cdata);
  76379. switch ($name) {
  76380. case 'name':
  76381. switch ($this->prev_element) {
  76382. case 'package':
  76383. $this->_packageInfo['package'] = $data;
  76384. break;
  76385. case 'maintainer':
  76386. $this->current_maintainer['name'] = $data;
  76387. break;
  76388. }
  76389. break;
  76390. case 'extends' :
  76391. $this->_packageInfo['extends'] = $data;
  76392. break;
  76393. case 'summary':
  76394. $this->_packageInfo['summary'] = $data;
  76395. break;
  76396. case 'description':
  76397. $data = $this->_unIndent($this->cdata);
  76398. $this->_packageInfo['description'] = $data;
  76399. break;
  76400. case 'user':
  76401. $this->current_maintainer['handle'] = $data;
  76402. break;
  76403. case 'email':
  76404. $this->current_maintainer['email'] = $data;
  76405. break;
  76406. case 'role':
  76407. $this->current_maintainer['role'] = $data;
  76408. break;
  76409. case 'version':
  76410. if ($this->in_changelog) {
  76411. $this->current_release['version'] = $data;
  76412. } else {
  76413. $this->_packageInfo['version'] = $data;
  76414. }
  76415. break;
  76416. case 'date':
  76417. if ($this->in_changelog) {
  76418. $this->current_release['release_date'] = $data;
  76419. } else {
  76420. $this->_packageInfo['release_date'] = $data;
  76421. }
  76422. break;
  76423. case 'notes':
  76424. // try to "de-indent" release notes in case someone
  76425. // has been over-indenting their xml ;-)
  76426. // Trim only on the right side
  76427. $data = rtrim($this->_unIndent($this->cdata));
  76428. if ($this->in_changelog) {
  76429. $this->current_release['release_notes'] = $data;
  76430. } else {
  76431. $this->_packageInfo['release_notes'] = $data;
  76432. }
  76433. break;
  76434. case 'warnings':
  76435. if ($this->in_changelog) {
  76436. $this->current_release['release_warnings'] = $data;
  76437. } else {
  76438. $this->_packageInfo['release_warnings'] = $data;
  76439. }
  76440. break;
  76441. case 'state':
  76442. if ($this->in_changelog) {
  76443. $this->current_release['release_state'] = $data;
  76444. } else {
  76445. $this->_packageInfo['release_state'] = $data;
  76446. }
  76447. break;
  76448. case 'license':
  76449. if ($this->in_changelog) {
  76450. $this->current_release['release_license'] = $data;
  76451. } else {
  76452. $this->_packageInfo['release_license'] = $data;
  76453. }
  76454. break;
  76455. case 'dep':
  76456. if ($data && !$this->in_changelog) {
  76457. $this->_packageInfo['release_deps'][$this->d_i]['name'] = $data;
  76458. }
  76459. break;
  76460. case 'dir':
  76461. if ($this->in_changelog) {
  76462. break;
  76463. }
  76464. array_pop($this->dir_names);
  76465. break;
  76466. case 'file':
  76467. if ($this->in_changelog) {
  76468. break;
  76469. }
  76470. if ($data) {
  76471. $path = '';
  76472. if (count($this->dir_names)) {
  76473. foreach ($this->dir_names as $dir) {
  76474. $path .= $dir . '/';
  76475. }
  76476. }
  76477. $path .= $data;
  76478. $this->filelist[$path] = $this->current_attributes;
  76479. // Set the baseinstalldir only if the file don't have this attrib
  76480. if (!isset($this->filelist[$path]['baseinstalldir']) &&
  76481. isset($this->dir_install))
  76482. {
  76483. $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
  76484. }
  76485. // Set the Role
  76486. if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
  76487. $this->filelist[$path]['role'] = $this->dir_role;
  76488. }
  76489. }
  76490. break;
  76491. case 'maintainer':
  76492. if (empty($this->_packageInfo['maintainers'][$this->m_i]['role'])) {
  76493. $this->_packageInfo['maintainers'][$this->m_i]['role'] = 'lead';
  76494. }
  76495. $this->m_i++;
  76496. break;
  76497. case 'release':
  76498. if ($this->in_changelog) {
  76499. $this->c_i++;
  76500. }
  76501. break;
  76502. case 'changelog':
  76503. $this->in_changelog = false;
  76504. break;
  76505. }
  76506. array_pop($this->element_stack);
  76507. $spos = sizeof($this->element_stack) - 1;
  76508. $this->current_element = ($spos > 0) ? $this->element_stack[$spos] : '';
  76509. $this->cdata = '';
  76510. }
  76511. // }}}
  76512. // {{{ _pkginfo_cdata_1_0()
  76513. /**
  76514. * XML parser callback for character data. Used for version 1.0
  76515. * packages.
  76516. *
  76517. * @param resource $xp XML parser resource
  76518. * @param string $name character data
  76519. *
  76520. * @return void
  76521. *
  76522. * @access private
  76523. */
  76524. function _pkginfo_cdata_1_0($xp, $data)
  76525. {
  76526. if (isset($this->cdata)) {
  76527. $this->cdata .= $data;
  76528. }
  76529. }
  76530. // }}}
  76531. }
  76532. ?><?php
  76533. /**
  76534. * package.xml parsing class, package.xml version 2.0
  76535. *
  76536. * PHP versions 4 and 5
  76537. *
  76538. * @category pear
  76539. * @package PEAR
  76540. * @author Greg Beaver <cellog@php.net>
  76541. * @copyright 1997-2009 The Authors
  76542. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  76543. * @link http://pear.php.net/package/PEAR
  76544. * @since File available since Release 1.4.0a1
  76545. */
  76546. /**
  76547. * base xml parser class
  76548. */
  76549. require_once 'phar://go-pear.phar/' . 'PEAR/XMLParser.php';
  76550. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/v2.php';
  76551. /**
  76552. * Parser for package.xml version 2.0
  76553. * @category pear
  76554. * @package PEAR
  76555. * @author Greg Beaver <cellog@php.net>
  76556. * @copyright 1997-2009 The Authors
  76557. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  76558. * @version Release: 1.10.16
  76559. * @link http://pear.php.net/package/PEAR
  76560. * @since Class available since Release 1.4.0a1
  76561. */
  76562. class PEAR_PackageFile_Parser_v2 extends PEAR_XMLParser
  76563. {
  76564. var $_config;
  76565. var $_logger;
  76566. var $_registry;
  76567. function setConfig(&$c)
  76568. {
  76569. $this->_config = &$c;
  76570. $this->_registry = &$c->getRegistry();
  76571. }
  76572. function setLogger(&$l)
  76573. {
  76574. $this->_logger = &$l;
  76575. }
  76576. /**
  76577. * Unindent given string
  76578. *
  76579. * @param string $str The string that has to be unindented.
  76580. * @return string
  76581. * @access private
  76582. */
  76583. function _unIndent($str)
  76584. {
  76585. // remove leading newlines
  76586. $str = preg_replace('/^[\r\n]+/', '', $str);
  76587. // find whitespace at the beginning of the first line
  76588. $indent_len = strspn($str, " \t");
  76589. $indent = substr($str, 0, $indent_len);
  76590. $data = '';
  76591. // remove the same amount of whitespace from following lines
  76592. foreach (explode("\n", $str) as $line) {
  76593. if (substr($line, 0, $indent_len) == $indent) {
  76594. $data .= substr($line, $indent_len) . "\n";
  76595. } else {
  76596. $data .= $line . "\n";
  76597. }
  76598. }
  76599. return $data;
  76600. }
  76601. /**
  76602. * post-process data
  76603. *
  76604. * @param string $data
  76605. * @param string $element element name
  76606. */
  76607. function postProcess($data, $element)
  76608. {
  76609. if ($element == 'notes') {
  76610. return trim($this->_unIndent($data));
  76611. }
  76612. return trim($data);
  76613. }
  76614. /**
  76615. * @param string
  76616. * @param string file name of the package.xml
  76617. * @param string|false name of the archive this package.xml came from, if any
  76618. * @param string class name to instantiate and return. This must be PEAR_PackageFile_v2 or
  76619. * a subclass
  76620. * @return PEAR_PackageFile_v2
  76621. */
  76622. function parse($data, $file = null, $archive = false, $class = 'PEAR_PackageFile_v2')
  76623. {
  76624. if (PEAR::isError($err = parent::parse($data))) {
  76625. return $err;
  76626. }
  76627. $ret = new $class;
  76628. $ret->encoding = $this->encoding;
  76629. $ret->setConfig($this->_config);
  76630. if (isset($this->_logger)) {
  76631. $ret->setLogger($this->_logger);
  76632. }
  76633. $ret->fromArray($this->_unserializedData);
  76634. $ret->setPackagefile($file, $archive);
  76635. return $ret;
  76636. }
  76637. }<?php
  76638. /**
  76639. * PEAR_PackageFile_v1, package.xml version 1.0
  76640. *
  76641. * PHP versions 4 and 5
  76642. *
  76643. * @category pear
  76644. * @package PEAR
  76645. * @author Greg Beaver <cellog@php.net>
  76646. * @copyright 1997-2009 The Authors
  76647. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  76648. * @link http://pear.php.net/package/PEAR
  76649. * @since File available since Release 1.4.0a1
  76650. */
  76651. /**
  76652. * For error handling
  76653. */
  76654. require_once 'phar://go-pear.phar/' . 'PEAR/ErrorStack.php';
  76655. /**
  76656. * Error code if parsing is attempted with no xml extension
  76657. */
  76658. define('PEAR_PACKAGEFILE_ERROR_NO_XML_EXT', 3);
  76659. /**
  76660. * Error code if creating the xml parser resource fails
  76661. */
  76662. define('PEAR_PACKAGEFILE_ERROR_CANT_MAKE_PARSER', 4);
  76663. /**
  76664. * Error code used for all sax xml parsing errors
  76665. */
  76666. define('PEAR_PACKAGEFILE_ERROR_PARSER_ERROR', 5);
  76667. /**
  76668. * Error code used when there is no name
  76669. */
  76670. define('PEAR_PACKAGEFILE_ERROR_NO_NAME', 6);
  76671. /**
  76672. * Error code when a package name is not valid
  76673. */
  76674. define('PEAR_PACKAGEFILE_ERROR_INVALID_NAME', 7);
  76675. /**
  76676. * Error code used when no summary is parsed
  76677. */
  76678. define('PEAR_PACKAGEFILE_ERROR_NO_SUMMARY', 8);
  76679. /**
  76680. * Error code for summaries that are more than 1 line
  76681. */
  76682. define('PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY', 9);
  76683. /**
  76684. * Error code used when no description is present
  76685. */
  76686. define('PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION', 10);
  76687. /**
  76688. * Error code used when no license is present
  76689. */
  76690. define('PEAR_PACKAGEFILE_ERROR_NO_LICENSE', 11);
  76691. /**
  76692. * Error code used when a <version> version number is not present
  76693. */
  76694. define('PEAR_PACKAGEFILE_ERROR_NO_VERSION', 12);
  76695. /**
  76696. * Error code used when a <version> version number is invalid
  76697. */
  76698. define('PEAR_PACKAGEFILE_ERROR_INVALID_VERSION', 13);
  76699. /**
  76700. * Error code when release state is missing
  76701. */
  76702. define('PEAR_PACKAGEFILE_ERROR_NO_STATE', 14);
  76703. /**
  76704. * Error code when release state is invalid
  76705. */
  76706. define('PEAR_PACKAGEFILE_ERROR_INVALID_STATE', 15);
  76707. /**
  76708. * Error code when release state is missing
  76709. */
  76710. define('PEAR_PACKAGEFILE_ERROR_NO_DATE', 16);
  76711. /**
  76712. * Error code when release state is invalid
  76713. */
  76714. define('PEAR_PACKAGEFILE_ERROR_INVALID_DATE', 17);
  76715. /**
  76716. * Error code when no release notes are found
  76717. */
  76718. define('PEAR_PACKAGEFILE_ERROR_NO_NOTES', 18);
  76719. /**
  76720. * Error code when no maintainers are found
  76721. */
  76722. define('PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS', 19);
  76723. /**
  76724. * Error code when a maintainer has no handle
  76725. */
  76726. define('PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE', 20);
  76727. /**
  76728. * Error code when a maintainer has no handle
  76729. */
  76730. define('PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE', 21);
  76731. /**
  76732. * Error code when a maintainer has no name
  76733. */
  76734. define('PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME', 22);
  76735. /**
  76736. * Error code when a maintainer has no email
  76737. */
  76738. define('PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL', 23);
  76739. /**
  76740. * Error code when a maintainer has no handle
  76741. */
  76742. define('PEAR_PACKAGEFILE_ERROR_INVALID_MAINTROLE', 24);
  76743. /**
  76744. * Error code when a dependency is not a PHP dependency, but has no name
  76745. */
  76746. define('PEAR_PACKAGEFILE_ERROR_NO_DEPNAME', 25);
  76747. /**
  76748. * Error code when a dependency has no type (pkg, php, etc.)
  76749. */
  76750. define('PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE', 26);
  76751. /**
  76752. * Error code when a dependency has no relation (lt, ge, has, etc.)
  76753. */
  76754. define('PEAR_PACKAGEFILE_ERROR_NO_DEPREL', 27);
  76755. /**
  76756. * Error code when a dependency is not a 'has' relation, but has no version
  76757. */
  76758. define('PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION', 28);
  76759. /**
  76760. * Error code when a dependency has an invalid relation
  76761. */
  76762. define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPREL', 29);
  76763. /**
  76764. * Error code when a dependency has an invalid type
  76765. */
  76766. define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPTYPE', 30);
  76767. /**
  76768. * Error code when a dependency has an invalid optional option
  76769. */
  76770. define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL', 31);
  76771. /**
  76772. * Error code when a dependency is a pkg dependency, and has an invalid package name
  76773. */
  76774. define('PEAR_PACKAGEFILE_ERROR_INVALID_DEPNAME', 32);
  76775. /**
  76776. * Error code when a dependency has a channel="foo" attribute, and foo is not a registered channel
  76777. */
  76778. define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_DEPCHANNEL', 33);
  76779. /**
  76780. * Error code when rel="has" and version attribute is present.
  76781. */
  76782. define('PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED', 34);
  76783. /**
  76784. * Error code when type="php" and dependency name is present
  76785. */
  76786. define('PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED', 35);
  76787. /**
  76788. * Error code when a configure option has no name
  76789. */
  76790. define('PEAR_PACKAGEFILE_ERROR_NO_CONFNAME', 36);
  76791. /**
  76792. * Error code when a configure option has no name
  76793. */
  76794. define('PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT', 37);
  76795. /**
  76796. * Error code when a file in the filelist has an invalid role
  76797. */
  76798. define('PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE', 38);
  76799. /**
  76800. * Error code when a file in the filelist has no role
  76801. */
  76802. define('PEAR_PACKAGEFILE_ERROR_NO_FILEROLE', 39);
  76803. /**
  76804. * Error code when analyzing a php source file that has parse errors
  76805. */
  76806. define('PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE', 40);
  76807. /**
  76808. * Error code when analyzing a php source file reveals a source element
  76809. * without a package name prefix
  76810. */
  76811. define('PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX', 41);
  76812. /**
  76813. * Error code when an unknown channel is specified
  76814. */
  76815. define('PEAR_PACKAGEFILE_ERROR_UNKNOWN_CHANNEL', 42);
  76816. /**
  76817. * Error code when no files are found in the filelist
  76818. */
  76819. define('PEAR_PACKAGEFILE_ERROR_NO_FILES', 43);
  76820. /**
  76821. * Error code when a file is not valid php according to _analyzeSourceCode()
  76822. */
  76823. define('PEAR_PACKAGEFILE_ERROR_INVALID_FILE', 44);
  76824. /**
  76825. * Error code when the channel validator returns an error or warning
  76826. */
  76827. define('PEAR_PACKAGEFILE_ERROR_CHANNELVAL', 45);
  76828. /**
  76829. * Error code when a php5 package is packaged in php4 (analysis doesn't work)
  76830. */
  76831. define('PEAR_PACKAGEFILE_ERROR_PHP5', 46);
  76832. /**
  76833. * Error code when a file is listed in package.xml but does not exist
  76834. */
  76835. define('PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND', 47);
  76836. /**
  76837. * Error code when a <dep type="php" rel="not"... is encountered (use rel="ne")
  76838. */
  76839. define('PEAR_PACKAGEFILE_PHP_NO_NOT', 48);
  76840. /**
  76841. * Error code when a package.xml contains non-ISO-8859-1 characters
  76842. */
  76843. define('PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS', 49);
  76844. /**
  76845. * Error code when a dependency is not a 'has' relation, but has no version
  76846. */
  76847. define('PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION', 50);
  76848. /**
  76849. * Error code when a package has no lead developer
  76850. */
  76851. define('PEAR_PACKAGEFILE_ERROR_NO_LEAD', 51);
  76852. /**
  76853. * Error code when a filename begins with "."
  76854. */
  76855. define('PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME', 52);
  76856. /**
  76857. * package.xml encapsulator
  76858. * @category pear
  76859. * @package PEAR
  76860. * @author Greg Beaver <cellog@php.net>
  76861. * @copyright 1997-2009 The Authors
  76862. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  76863. * @version Release: 1.10.16
  76864. * @link http://pear.php.net/package/PEAR
  76865. * @since Class available since Release 1.4.0a1
  76866. */
  76867. class PEAR_PackageFile_v1
  76868. {
  76869. /**
  76870. * @access private
  76871. * @var PEAR_ErrorStack
  76872. * @access private
  76873. */
  76874. var $_stack;
  76875. /**
  76876. * A registry object, used to access the package name validation regex for non-standard channels
  76877. * @var PEAR_Registry
  76878. * @access private
  76879. */
  76880. var $_registry;
  76881. /**
  76882. * An object that contains a log method that matches PEAR_Common::log's signature
  76883. * @var object
  76884. * @access private
  76885. */
  76886. var $_logger;
  76887. /**
  76888. * Parsed package information
  76889. * @var array
  76890. * @access private
  76891. */
  76892. var $_packageInfo;
  76893. /**
  76894. * path to package.xml
  76895. * @var string
  76896. * @access private
  76897. */
  76898. var $_packageFile;
  76899. /**
  76900. * path to package .tgz or false if this is a local/extracted package.xml
  76901. * @var string
  76902. * @access private
  76903. */
  76904. var $_archiveFile;
  76905. /**
  76906. * @var int
  76907. * @access private
  76908. */
  76909. var $_isValid = 0;
  76910. /**
  76911. * Determines whether this packagefile was initialized only with partial package info
  76912. *
  76913. * If this package file was constructed via parsing REST, it will only contain
  76914. *
  76915. * - package name
  76916. * - channel name
  76917. * - dependencies
  76918. * @var boolean
  76919. * @access private
  76920. */
  76921. var $_incomplete = true;
  76922. /**
  76923. * @param bool determines whether to return a PEAR_Error object, or use the PEAR_ErrorStack
  76924. * @param string Name of Error Stack class to use.
  76925. */
  76926. function __construct()
  76927. {
  76928. $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v1');
  76929. $this->_stack->setErrorMessageTemplate($this->_getErrorMessage());
  76930. $this->_isValid = 0;
  76931. }
  76932. function installBinary($installer)
  76933. {
  76934. return false;
  76935. }
  76936. function isExtension($name)
  76937. {
  76938. return false;
  76939. }
  76940. function setConfig(&$config)
  76941. {
  76942. $this->_config = &$config;
  76943. $this->_registry = &$config->getRegistry();
  76944. }
  76945. function setRequestedGroup()
  76946. {
  76947. // placeholder
  76948. }
  76949. /**
  76950. * For saving in the registry.
  76951. *
  76952. * Set the last version that was installed
  76953. * @param string
  76954. */
  76955. function setLastInstalledVersion($version)
  76956. {
  76957. $this->_packageInfo['_lastversion'] = $version;
  76958. }
  76959. /**
  76960. * @return string|false
  76961. */
  76962. function getLastInstalledVersion()
  76963. {
  76964. if (isset($this->_packageInfo['_lastversion'])) {
  76965. return $this->_packageInfo['_lastversion'];
  76966. }
  76967. return false;
  76968. }
  76969. function getInstalledBinary()
  76970. {
  76971. return false;
  76972. }
  76973. function listPostinstallScripts()
  76974. {
  76975. return false;
  76976. }
  76977. function initPostinstallScripts()
  76978. {
  76979. return false;
  76980. }
  76981. function setLogger(&$logger)
  76982. {
  76983. if ($logger && (!is_object($logger) || !method_exists($logger, 'log'))) {
  76984. return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
  76985. }
  76986. $this->_logger = &$logger;
  76987. }
  76988. function setPackagefile($file, $archive = false)
  76989. {
  76990. $this->_packageFile = $file;
  76991. $this->_archiveFile = $archive ? $archive : $file;
  76992. }
  76993. function getPackageFile()
  76994. {
  76995. return isset($this->_packageFile) ? $this->_packageFile : false;
  76996. }
  76997. function getPackageType()
  76998. {
  76999. return 'php';
  77000. }
  77001. function getArchiveFile()
  77002. {
  77003. return $this->_archiveFile;
  77004. }
  77005. function packageInfo($field)
  77006. {
  77007. if (!is_string($field) || empty($field) ||
  77008. !isset($this->_packageInfo[$field])) {
  77009. return false;
  77010. }
  77011. return $this->_packageInfo[$field];
  77012. }
  77013. function setDirtree($path)
  77014. {
  77015. if (!isset($this->_packageInfo['dirtree'])) {
  77016. $this->_packageInfo['dirtree'] = array();
  77017. }
  77018. $this->_packageInfo['dirtree'][$path] = true;
  77019. }
  77020. function getDirtree()
  77021. {
  77022. if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
  77023. return $this->_packageInfo['dirtree'];
  77024. }
  77025. return false;
  77026. }
  77027. function resetDirtree()
  77028. {
  77029. unset($this->_packageInfo['dirtree']);
  77030. }
  77031. function fromArray($pinfo)
  77032. {
  77033. $this->_incomplete = false;
  77034. $this->_packageInfo = $pinfo;
  77035. }
  77036. function isIncomplete()
  77037. {
  77038. return $this->_incomplete;
  77039. }
  77040. function getChannel()
  77041. {
  77042. return 'pear.php.net';
  77043. }
  77044. function getUri()
  77045. {
  77046. return false;
  77047. }
  77048. function getTime()
  77049. {
  77050. return false;
  77051. }
  77052. function getExtends()
  77053. {
  77054. if (isset($this->_packageInfo['extends'])) {
  77055. return $this->_packageInfo['extends'];
  77056. }
  77057. return false;
  77058. }
  77059. /**
  77060. * @return array
  77061. */
  77062. function toArray()
  77063. {
  77064. if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
  77065. return false;
  77066. }
  77067. return $this->getArray();
  77068. }
  77069. function getArray()
  77070. {
  77071. return $this->_packageInfo;
  77072. }
  77073. function getName()
  77074. {
  77075. return $this->getPackage();
  77076. }
  77077. function getPackage()
  77078. {
  77079. if (isset($this->_packageInfo['package'])) {
  77080. return $this->_packageInfo['package'];
  77081. }
  77082. return false;
  77083. }
  77084. /**
  77085. * WARNING - don't use this unless you know what you are doing
  77086. */
  77087. function setRawPackage($package)
  77088. {
  77089. $this->_packageInfo['package'] = $package;
  77090. }
  77091. function setPackage($package)
  77092. {
  77093. $this->_packageInfo['package'] = $package;
  77094. $this->_isValid = false;
  77095. }
  77096. function getVersion()
  77097. {
  77098. if (isset($this->_packageInfo['version'])) {
  77099. return $this->_packageInfo['version'];
  77100. }
  77101. return false;
  77102. }
  77103. function setVersion($version)
  77104. {
  77105. $this->_packageInfo['version'] = $version;
  77106. $this->_isValid = false;
  77107. }
  77108. function clearMaintainers()
  77109. {
  77110. unset($this->_packageInfo['maintainers']);
  77111. }
  77112. function getMaintainers()
  77113. {
  77114. if (isset($this->_packageInfo['maintainers'])) {
  77115. return $this->_packageInfo['maintainers'];
  77116. }
  77117. return false;
  77118. }
  77119. /**
  77120. * Adds a new maintainer - no checking of duplicates is performed, use
  77121. * updatemaintainer for that purpose.
  77122. */
  77123. function addMaintainer($role, $handle, $name, $email)
  77124. {
  77125. $this->_packageInfo['maintainers'][] =
  77126. array('handle' => $handle, 'role' => $role, 'email' => $email, 'name' => $name);
  77127. $this->_isValid = false;
  77128. }
  77129. function updateMaintainer($role, $handle, $name, $email)
  77130. {
  77131. $found = false;
  77132. if (!isset($this->_packageInfo['maintainers']) ||
  77133. !is_array($this->_packageInfo['maintainers'])) {
  77134. return $this->addMaintainer($role, $handle, $name, $email);
  77135. }
  77136. foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) {
  77137. if ($maintainer['handle'] == $handle) {
  77138. $found = $i;
  77139. break;
  77140. }
  77141. }
  77142. if ($found !== false) {
  77143. unset($this->_packageInfo['maintainers'][$found]);
  77144. $this->_packageInfo['maintainers'] =
  77145. array_values($this->_packageInfo['maintainers']);
  77146. }
  77147. $this->addMaintainer($role, $handle, $name, $email);
  77148. }
  77149. function deleteMaintainer($handle)
  77150. {
  77151. $found = false;
  77152. foreach ($this->_packageInfo['maintainers'] as $i => $maintainer) {
  77153. if ($maintainer['handle'] == $handle) {
  77154. $found = $i;
  77155. break;
  77156. }
  77157. }
  77158. if ($found !== false) {
  77159. unset($this->_packageInfo['maintainers'][$found]);
  77160. $this->_packageInfo['maintainers'] =
  77161. array_values($this->_packageInfo['maintainers']);
  77162. return true;
  77163. }
  77164. return false;
  77165. }
  77166. function getState()
  77167. {
  77168. if (isset($this->_packageInfo['release_state'])) {
  77169. return $this->_packageInfo['release_state'];
  77170. }
  77171. return false;
  77172. }
  77173. function setRawState($state)
  77174. {
  77175. $this->_packageInfo['release_state'] = $state;
  77176. }
  77177. function setState($state)
  77178. {
  77179. $this->_packageInfo['release_state'] = $state;
  77180. $this->_isValid = false;
  77181. }
  77182. function getDate()
  77183. {
  77184. if (isset($this->_packageInfo['release_date'])) {
  77185. return $this->_packageInfo['release_date'];
  77186. }
  77187. return false;
  77188. }
  77189. function setDate($date)
  77190. {
  77191. $this->_packageInfo['release_date'] = $date;
  77192. $this->_isValid = false;
  77193. }
  77194. function getLicense()
  77195. {
  77196. if (isset($this->_packageInfo['release_license'])) {
  77197. return $this->_packageInfo['release_license'];
  77198. }
  77199. return false;
  77200. }
  77201. function setLicense($date)
  77202. {
  77203. $this->_packageInfo['release_license'] = $date;
  77204. $this->_isValid = false;
  77205. }
  77206. function getSummary()
  77207. {
  77208. if (isset($this->_packageInfo['summary'])) {
  77209. return $this->_packageInfo['summary'];
  77210. }
  77211. return false;
  77212. }
  77213. function setSummary($summary)
  77214. {
  77215. $this->_packageInfo['summary'] = $summary;
  77216. $this->_isValid = false;
  77217. }
  77218. function getDescription()
  77219. {
  77220. if (isset($this->_packageInfo['description'])) {
  77221. return $this->_packageInfo['description'];
  77222. }
  77223. return false;
  77224. }
  77225. function setDescription($desc)
  77226. {
  77227. $this->_packageInfo['description'] = $desc;
  77228. $this->_isValid = false;
  77229. }
  77230. function getNotes()
  77231. {
  77232. if (isset($this->_packageInfo['release_notes'])) {
  77233. return $this->_packageInfo['release_notes'];
  77234. }
  77235. return false;
  77236. }
  77237. function setNotes($notes)
  77238. {
  77239. $this->_packageInfo['release_notes'] = $notes;
  77240. $this->_isValid = false;
  77241. }
  77242. function getDeps()
  77243. {
  77244. if (isset($this->_packageInfo['release_deps'])) {
  77245. return $this->_packageInfo['release_deps'];
  77246. }
  77247. return false;
  77248. }
  77249. /**
  77250. * Reset dependencies prior to adding new ones
  77251. */
  77252. function clearDeps()
  77253. {
  77254. unset($this->_packageInfo['release_deps']);
  77255. }
  77256. function addPhpDep($version, $rel)
  77257. {
  77258. $this->_isValid = false;
  77259. $this->_packageInfo['release_deps'][] =
  77260. array('type' => 'php',
  77261. 'rel' => $rel,
  77262. 'version' => $version);
  77263. }
  77264. function addPackageDep($name, $version, $rel, $optional = 'no')
  77265. {
  77266. $this->_isValid = false;
  77267. $dep =
  77268. array('type' => 'pkg',
  77269. 'name' => $name,
  77270. 'rel' => $rel,
  77271. 'optional' => $optional);
  77272. if ($rel != 'has' && $rel != 'not') {
  77273. $dep['version'] = $version;
  77274. }
  77275. $this->_packageInfo['release_deps'][] = $dep;
  77276. }
  77277. function addExtensionDep($name, $version, $rel, $optional = 'no')
  77278. {
  77279. $this->_isValid = false;
  77280. $this->_packageInfo['release_deps'][] =
  77281. array('type' => 'ext',
  77282. 'name' => $name,
  77283. 'rel' => $rel,
  77284. 'version' => $version,
  77285. 'optional' => $optional);
  77286. }
  77287. /**
  77288. * WARNING - do not use this function directly unless you know what you're doing
  77289. */
  77290. function setDeps($deps)
  77291. {
  77292. $this->_packageInfo['release_deps'] = $deps;
  77293. }
  77294. function hasDeps()
  77295. {
  77296. return isset($this->_packageInfo['release_deps']) &&
  77297. count($this->_packageInfo['release_deps']);
  77298. }
  77299. function getDependencyGroup($group)
  77300. {
  77301. return false;
  77302. }
  77303. function isCompatible($pf)
  77304. {
  77305. return false;
  77306. }
  77307. function isSubpackageOf($p)
  77308. {
  77309. return $p->isSubpackage($this);
  77310. }
  77311. function isSubpackage($p)
  77312. {
  77313. return false;
  77314. }
  77315. function dependsOn($package, $channel)
  77316. {
  77317. if (strtolower($channel) != 'pear.php.net') {
  77318. return false;
  77319. }
  77320. if (!($deps = $this->getDeps())) {
  77321. return false;
  77322. }
  77323. foreach ($deps as $dep) {
  77324. if ($dep['type'] != 'pkg') {
  77325. continue;
  77326. }
  77327. if (strtolower($dep['name']) == strtolower($package)) {
  77328. return true;
  77329. }
  77330. }
  77331. return false;
  77332. }
  77333. function getConfigureOptions()
  77334. {
  77335. if (isset($this->_packageInfo['configure_options'])) {
  77336. return $this->_packageInfo['configure_options'];
  77337. }
  77338. return false;
  77339. }
  77340. function hasConfigureOptions()
  77341. {
  77342. return isset($this->_packageInfo['configure_options']) &&
  77343. count($this->_packageInfo['configure_options']);
  77344. }
  77345. function addConfigureOption($name, $prompt, $default = false)
  77346. {
  77347. $o = array('name' => $name, 'prompt' => $prompt);
  77348. if ($default !== false) {
  77349. $o['default'] = $default;
  77350. }
  77351. if (!isset($this->_packageInfo['configure_options'])) {
  77352. $this->_packageInfo['configure_options'] = array();
  77353. }
  77354. $this->_packageInfo['configure_options'][] = $o;
  77355. }
  77356. function clearConfigureOptions()
  77357. {
  77358. unset($this->_packageInfo['configure_options']);
  77359. }
  77360. function getProvides()
  77361. {
  77362. if (isset($this->_packageInfo['provides'])) {
  77363. return $this->_packageInfo['provides'];
  77364. }
  77365. return false;
  77366. }
  77367. function getProvidesExtension()
  77368. {
  77369. return false;
  77370. }
  77371. function addFile($dir, $file, $attrs)
  77372. {
  77373. $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir);
  77374. if ($dir == '/' || $dir == '') {
  77375. $dir = '';
  77376. } else {
  77377. $dir .= '/';
  77378. }
  77379. $file = $dir . $file;
  77380. $file = preg_replace('![\\/]+!', '/', $file);
  77381. $this->_packageInfo['filelist'][$file] = $attrs;
  77382. }
  77383. function getInstallationFilelist()
  77384. {
  77385. return $this->getFilelist();
  77386. }
  77387. function getFilelist()
  77388. {
  77389. if (isset($this->_packageInfo['filelist'])) {
  77390. return $this->_packageInfo['filelist'];
  77391. }
  77392. return false;
  77393. }
  77394. function setFileAttribute($file, $attr, $value)
  77395. {
  77396. $this->_packageInfo['filelist'][$file][$attr] = $value;
  77397. }
  77398. function resetFilelist()
  77399. {
  77400. $this->_packageInfo['filelist'] = array();
  77401. }
  77402. function setInstalledAs($file, $path)
  77403. {
  77404. if ($path) {
  77405. return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
  77406. }
  77407. unset($this->_packageInfo['filelist'][$file]['installed_as']);
  77408. }
  77409. function installedFile($file, $atts)
  77410. {
  77411. if (isset($this->_packageInfo['filelist'][$file])) {
  77412. $this->_packageInfo['filelist'][$file] =
  77413. array_merge($this->_packageInfo['filelist'][$file], $atts);
  77414. } else {
  77415. $this->_packageInfo['filelist'][$file] = $atts;
  77416. }
  77417. }
  77418. function getChangelog()
  77419. {
  77420. if (isset($this->_packageInfo['changelog'])) {
  77421. return $this->_packageInfo['changelog'];
  77422. }
  77423. return false;
  77424. }
  77425. function getPackagexmlVersion()
  77426. {
  77427. return '1.0';
  77428. }
  77429. /**
  77430. * Wrapper to {@link PEAR_ErrorStack::getErrors()}
  77431. * @param boolean determines whether to purge the error stack after retrieving
  77432. * @return array
  77433. */
  77434. function getValidationWarnings($purge = true)
  77435. {
  77436. return $this->_stack->getErrors($purge);
  77437. }
  77438. // }}}
  77439. /**
  77440. * Validation error. Also marks the object contents as invalid
  77441. * @param error code
  77442. * @param array error information
  77443. * @access private
  77444. */
  77445. function _validateError($code, $params = array())
  77446. {
  77447. $this->_stack->push($code, 'error', $params, false, false, debug_backtrace());
  77448. $this->_isValid = false;
  77449. }
  77450. /**
  77451. * Validation warning. Does not mark the object contents invalid.
  77452. * @param error code
  77453. * @param array error information
  77454. * @access private
  77455. */
  77456. function _validateWarning($code, $params = array())
  77457. {
  77458. $this->_stack->push($code, 'warning', $params, false, false, debug_backtrace());
  77459. }
  77460. /**
  77461. * @param integer error code
  77462. * @access protected
  77463. */
  77464. function _getErrorMessage()
  77465. {
  77466. return array(
  77467. PEAR_PACKAGEFILE_ERROR_NO_NAME =>
  77468. 'Missing Package Name',
  77469. PEAR_PACKAGEFILE_ERROR_NO_SUMMARY =>
  77470. 'No summary found',
  77471. PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY =>
  77472. 'Summary should be on one line',
  77473. PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION =>
  77474. 'Missing description',
  77475. PEAR_PACKAGEFILE_ERROR_NO_LICENSE =>
  77476. 'Missing license',
  77477. PEAR_PACKAGEFILE_ERROR_NO_VERSION =>
  77478. 'No release version found',
  77479. PEAR_PACKAGEFILE_ERROR_NO_STATE =>
  77480. 'No release state found',
  77481. PEAR_PACKAGEFILE_ERROR_NO_DATE =>
  77482. 'No release date found',
  77483. PEAR_PACKAGEFILE_ERROR_NO_NOTES =>
  77484. 'No release notes found',
  77485. PEAR_PACKAGEFILE_ERROR_NO_LEAD =>
  77486. 'Package must have at least one lead maintainer',
  77487. PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS =>
  77488. 'No maintainers found, at least one must be defined',
  77489. PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE =>
  77490. 'Maintainer %index% has no handle (user ID at channel server)',
  77491. PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE =>
  77492. 'Maintainer %index% has no role',
  77493. PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME =>
  77494. 'Maintainer %index% has no name',
  77495. PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL =>
  77496. 'Maintainer %index% has no email',
  77497. PEAR_PACKAGEFILE_ERROR_NO_DEPNAME =>
  77498. 'Dependency %index% is not a php dependency, and has no name',
  77499. PEAR_PACKAGEFILE_ERROR_NO_DEPREL =>
  77500. 'Dependency %index% has no relation (rel)',
  77501. PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE =>
  77502. 'Dependency %index% has no type',
  77503. PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED =>
  77504. 'PHP Dependency %index% has a name attribute of "%name%" which will be' .
  77505. ' ignored!',
  77506. PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION =>
  77507. 'Dependency %index% is not a rel="has" or rel="not" dependency, ' .
  77508. 'and has no version',
  77509. PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION =>
  77510. 'Dependency %index% is a type="php" dependency, ' .
  77511. 'and has no version',
  77512. PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED =>
  77513. 'Dependency %index% is a rel="%rel%" dependency, versioning is ignored',
  77514. PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL =>
  77515. 'Dependency %index% has invalid optional value "%opt%", should be yes or no',
  77516. PEAR_PACKAGEFILE_PHP_NO_NOT =>
  77517. 'Dependency %index%: php dependencies cannot use "not" rel, use "ne"' .
  77518. ' to exclude specific versions',
  77519. PEAR_PACKAGEFILE_ERROR_NO_CONFNAME =>
  77520. 'Configure Option %index% has no name',
  77521. PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT =>
  77522. 'Configure Option %index% has no prompt',
  77523. PEAR_PACKAGEFILE_ERROR_NO_FILES =>
  77524. 'No files in <filelist> section of package.xml',
  77525. PEAR_PACKAGEFILE_ERROR_NO_FILEROLE =>
  77526. 'File "%file%" has no role, expecting one of "%roles%"',
  77527. PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE =>
  77528. 'File "%file%" has invalid role "%role%", expecting one of "%roles%"',
  77529. PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME =>
  77530. 'File "%file%" cannot start with ".", cannot package or install',
  77531. PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE =>
  77532. 'Parser error: invalid PHP found in file "%file%"',
  77533. PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX =>
  77534. 'in %file%: %type% "%name%" not prefixed with package name "%package%"',
  77535. PEAR_PACKAGEFILE_ERROR_INVALID_FILE =>
  77536. 'Parser error: invalid PHP file "%file%"',
  77537. PEAR_PACKAGEFILE_ERROR_CHANNELVAL =>
  77538. 'Channel validator error: field "%field%" - %reason%',
  77539. PEAR_PACKAGEFILE_ERROR_PHP5 =>
  77540. 'Error, PHP5 token encountered in %file%, analysis should be in PHP5',
  77541. PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND =>
  77542. 'File "%file%" in package.xml does not exist',
  77543. PEAR_PACKAGEFILE_ERROR_NON_ISO_CHARS =>
  77544. 'Package.xml contains non-ISO-8859-1 characters, and may not validate',
  77545. );
  77546. }
  77547. /**
  77548. * Validate XML package definition file.
  77549. *
  77550. * @access public
  77551. * @return boolean
  77552. */
  77553. function validate($state = PEAR_VALIDATE_NORMAL, $nofilechecking = false)
  77554. {
  77555. if (($this->_isValid & $state) == $state) {
  77556. return true;
  77557. }
  77558. $this->_isValid = true;
  77559. $info = $this->_packageInfo;
  77560. if (empty($info['package'])) {
  77561. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NAME);
  77562. $this->_packageName = $pn = 'unknown';
  77563. } else {
  77564. $this->_packageName = $pn = $info['package'];
  77565. }
  77566. if (empty($info['summary'])) {
  77567. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_SUMMARY);
  77568. } elseif (strpos(trim($info['summary']), "\n") !== false) {
  77569. $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_MULTILINE_SUMMARY,
  77570. array('summary' => $info['summary']));
  77571. }
  77572. if (empty($info['description'])) {
  77573. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DESCRIPTION);
  77574. }
  77575. if (empty($info['release_license'])) {
  77576. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LICENSE);
  77577. }
  77578. if (empty($info['version'])) {
  77579. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_VERSION);
  77580. }
  77581. if (empty($info['release_state'])) {
  77582. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_STATE);
  77583. }
  77584. if (empty($info['release_date'])) {
  77585. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DATE);
  77586. }
  77587. if (empty($info['release_notes'])) {
  77588. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_NOTES);
  77589. }
  77590. if (empty($info['maintainers'])) {
  77591. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTAINERS);
  77592. } else {
  77593. $haslead = false;
  77594. $i = 1;
  77595. foreach ($info['maintainers'] as $m) {
  77596. if (empty($m['handle'])) {
  77597. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTHANDLE,
  77598. array('index' => $i));
  77599. }
  77600. if (empty($m['role'])) {
  77601. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTROLE,
  77602. array('index' => $i, 'roles' => PEAR_Common::getUserRoles()));
  77603. } elseif ($m['role'] == 'lead') {
  77604. $haslead = true;
  77605. }
  77606. if (empty($m['name'])) {
  77607. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTNAME,
  77608. array('index' => $i));
  77609. }
  77610. if (empty($m['email'])) {
  77611. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_MAINTEMAIL,
  77612. array('index' => $i));
  77613. }
  77614. $i++;
  77615. }
  77616. if (!$haslead) {
  77617. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_LEAD);
  77618. }
  77619. }
  77620. if (!empty($info['release_deps'])) {
  77621. $i = 1;
  77622. foreach ($info['release_deps'] as $d) {
  77623. if (!isset($d['type']) || empty($d['type'])) {
  77624. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPTYPE,
  77625. array('index' => $i, 'types' => PEAR_Common::getDependencyTypes()));
  77626. continue;
  77627. }
  77628. if (!isset($d['rel']) || empty($d['rel'])) {
  77629. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPREL,
  77630. array('index' => $i, 'rels' => PEAR_Common::getDependencyRelations()));
  77631. continue;
  77632. }
  77633. if (!empty($d['optional'])) {
  77634. if (!in_array($d['optional'], array('yes', 'no'))) {
  77635. $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_DEPOPTIONAL,
  77636. array('index' => $i, 'opt' => $d['optional']));
  77637. }
  77638. }
  77639. if ($d['rel'] != 'has' && $d['rel'] != 'not' && empty($d['version'])) {
  77640. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPVERSION,
  77641. array('index' => $i));
  77642. } elseif (($d['rel'] == 'has' || $d['rel'] == 'not') && !empty($d['version'])) {
  77643. $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPVERSION_IGNORED,
  77644. array('index' => $i, 'rel' => $d['rel']));
  77645. }
  77646. if ($d['type'] == 'php' && !empty($d['name'])) {
  77647. $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_DEPNAME_IGNORED,
  77648. array('index' => $i, 'name' => $d['name']));
  77649. } elseif ($d['type'] != 'php' && empty($d['name'])) {
  77650. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPNAME,
  77651. array('index' => $i));
  77652. }
  77653. if ($d['type'] == 'php' && empty($d['version'])) {
  77654. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_DEPPHPVERSION,
  77655. array('index' => $i));
  77656. }
  77657. if (($d['rel'] == 'not') && ($d['type'] == 'php')) {
  77658. $this->_validateError(PEAR_PACKAGEFILE_PHP_NO_NOT,
  77659. array('index' => $i));
  77660. }
  77661. $i++;
  77662. }
  77663. }
  77664. if (!empty($info['configure_options'])) {
  77665. $i = 1;
  77666. foreach ($info['configure_options'] as $c) {
  77667. if (empty($c['name'])) {
  77668. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFNAME,
  77669. array('index' => $i));
  77670. }
  77671. if (empty($c['prompt'])) {
  77672. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_CONFPROMPT,
  77673. array('index' => $i));
  77674. }
  77675. $i++;
  77676. }
  77677. }
  77678. if (empty($info['filelist'])) {
  77679. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILES);
  77680. $errors[] = 'no files';
  77681. } else {
  77682. foreach ($info['filelist'] as $file => $fa) {
  77683. if (empty($fa['role'])) {
  77684. $this->_validateError(PEAR_PACKAGEFILE_ERROR_NO_FILEROLE,
  77685. array('file' => $file, 'roles' => PEAR_Common::getFileRoles()));
  77686. continue;
  77687. } elseif (!in_array($fa['role'], PEAR_Common::getFileRoles())) {
  77688. $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILEROLE,
  77689. array('file' => $file, 'role' => $fa['role'], 'roles' => PEAR_Common::getFileRoles()));
  77690. }
  77691. if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~', str_replace('\\', '/', $file))) {
  77692. // file contains .. parent directory or . cur directory references
  77693. $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
  77694. array('file' => $file));
  77695. }
  77696. if (isset($fa['install-as']) &&
  77697. preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
  77698. str_replace('\\', '/', $fa['install-as']))) {
  77699. // install-as contains .. parent directory or . cur directory references
  77700. $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
  77701. array('file' => $file . ' [installed as ' . $fa['install-as'] . ']'));
  77702. }
  77703. if (isset($fa['baseinstalldir']) &&
  77704. preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
  77705. str_replace('\\', '/', $fa['baseinstalldir']))) {
  77706. // install-as contains .. parent directory or . cur directory references
  77707. $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_FILENAME,
  77708. array('file' => $file . ' [baseinstalldir ' . $fa['baseinstalldir'] . ']'));
  77709. }
  77710. }
  77711. }
  77712. if (isset($this->_registry) && $this->_isValid) {
  77713. $chan = $this->_registry->getChannel('pear.php.net');
  77714. if (PEAR::isError($chan)) {
  77715. $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $chan->getMessage());
  77716. return $this->_isValid = 0;
  77717. }
  77718. $validator = $chan->getValidationObject();
  77719. $validator->setPackageFile($this);
  77720. $validator->validate($state);
  77721. $failures = $validator->getFailures();
  77722. foreach ($failures['errors'] as $error) {
  77723. $this->_validateError(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $error);
  77724. }
  77725. foreach ($failures['warnings'] as $warning) {
  77726. $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_CHANNELVAL, $warning);
  77727. }
  77728. }
  77729. if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$nofilechecking) {
  77730. if ($this->_analyzePhpFiles()) {
  77731. $this->_isValid = true;
  77732. }
  77733. }
  77734. if ($this->_isValid) {
  77735. return $this->_isValid = $state;
  77736. }
  77737. return $this->_isValid = 0;
  77738. }
  77739. function _analyzePhpFiles()
  77740. {
  77741. if (!$this->_isValid) {
  77742. return false;
  77743. }
  77744. if (!isset($this->_packageFile)) {
  77745. return false;
  77746. }
  77747. $dir_prefix = dirname($this->_packageFile);
  77748. $common = new PEAR_Common;
  77749. $log = isset($this->_logger) ? array(&$this->_logger, 'log') :
  77750. array($common, 'log');
  77751. $info = $this->getFilelist();
  77752. foreach ($info as $file => $fa) {
  77753. if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
  77754. $this->_validateError(PEAR_PACKAGEFILE_ERROR_FILE_NOTFOUND,
  77755. array('file' => realpath($dir_prefix) . DIRECTORY_SEPARATOR . $file));
  77756. continue;
  77757. }
  77758. if ($fa['role'] == 'php' && $dir_prefix) {
  77759. call_user_func_array($log, array(1, "Analyzing $file"));
  77760. $srcinfo = $this->_analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
  77761. if ($srcinfo) {
  77762. $this->_buildProvidesArray($srcinfo);
  77763. }
  77764. }
  77765. }
  77766. $this->_packageName = $pn = $this->getPackage();
  77767. $pnl = strlen($pn);
  77768. if (isset($this->_packageInfo['provides'])) {
  77769. foreach ((array) $this->_packageInfo['provides'] as $key => $what) {
  77770. if (isset($what['explicit'])) {
  77771. // skip conformance checks if the provides entry is
  77772. // specified in the package.xml file
  77773. continue;
  77774. }
  77775. extract($what);
  77776. if ($type == 'class') {
  77777. if (!strncasecmp($name, $pn, $pnl)) {
  77778. continue;
  77779. }
  77780. $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX,
  77781. array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn));
  77782. } elseif ($type == 'function') {
  77783. if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
  77784. continue;
  77785. }
  77786. $this->_validateWarning(PEAR_PACKAGEFILE_ERROR_NO_PNAME_PREFIX,
  77787. array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn));
  77788. }
  77789. }
  77790. }
  77791. return $this->_isValid;
  77792. }
  77793. /**
  77794. * Get the default xml generator object
  77795. *
  77796. * @return PEAR_PackageFile_Generator_v1
  77797. */
  77798. function &getDefaultGenerator()
  77799. {
  77800. if (!class_exists('PEAR_PackageFile_Generator_v1')) {
  77801. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/Generator/v1.php';
  77802. }
  77803. $a = new PEAR_PackageFile_Generator_v1($this);
  77804. return $a;
  77805. }
  77806. /**
  77807. * Get the contents of a file listed within the package.xml
  77808. * @param string
  77809. * @return string
  77810. */
  77811. function getFileContents($file)
  77812. {
  77813. if ($this->_archiveFile == $this->_packageFile) { // unpacked
  77814. $dir = dirname($this->_packageFile);
  77815. $file = $dir . DIRECTORY_SEPARATOR . $file;
  77816. $file = str_replace(array('/', '\\'),
  77817. array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
  77818. if (file_exists($file) && is_readable($file)) {
  77819. return implode('', file($file));
  77820. }
  77821. } else { // tgz
  77822. if (!class_exists('Archive_Tar')) {
  77823. require_once 'phar://go-pear.phar/' . 'Archive/Tar.php';
  77824. }
  77825. $tar = new Archive_Tar($this->_archiveFile);
  77826. $tar->pushErrorHandling(PEAR_ERROR_RETURN);
  77827. if ($file != 'package.xml' && $file != 'package2.xml') {
  77828. $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
  77829. }
  77830. $file = $tar->extractInString($file);
  77831. $tar->popErrorHandling();
  77832. if (PEAR::isError($file)) {
  77833. return PEAR::raiseError("Cannot locate file '$file' in archive");
  77834. }
  77835. return $file;
  77836. }
  77837. }
  77838. // {{{ analyzeSourceCode()
  77839. /**
  77840. * Analyze the source code of the given PHP file
  77841. *
  77842. * @param string Filename of the PHP file
  77843. * @return mixed
  77844. * @access private
  77845. */
  77846. function _analyzeSourceCode($file)
  77847. {
  77848. if (!function_exists("token_get_all")) {
  77849. return false;
  77850. }
  77851. if (!defined('T_DOC_COMMENT')) {
  77852. define('T_DOC_COMMENT', T_COMMENT);
  77853. }
  77854. if (!defined('T_INTERFACE')) {
  77855. define('T_INTERFACE', -1);
  77856. }
  77857. if (!defined('T_IMPLEMENTS')) {
  77858. define('T_IMPLEMENTS', -1);
  77859. }
  77860. if (!$fp = @fopen($file, "r")) {
  77861. return false;
  77862. }
  77863. fclose($fp);
  77864. $contents = file_get_contents($file);
  77865. $tokens = token_get_all($contents);
  77866. /*
  77867. for ($i = 0; $i < sizeof($tokens); $i++) {
  77868. @list($token, $data) = $tokens[$i];
  77869. if (is_string($token)) {
  77870. var_dump($token);
  77871. } else {
  77872. print token_name($token) . ' ';
  77873. var_dump(rtrim($data));
  77874. }
  77875. }
  77876. */
  77877. $look_for = 0;
  77878. $paren_level = 0;
  77879. $bracket_level = 0;
  77880. $brace_level = 0;
  77881. $lastphpdoc = '';
  77882. $current_class = '';
  77883. $current_interface = '';
  77884. $current_class_level = -1;
  77885. $current_function = '';
  77886. $current_function_level = -1;
  77887. $declared_classes = array();
  77888. $declared_interfaces = array();
  77889. $declared_functions = array();
  77890. $declared_methods = array();
  77891. $used_classes = array();
  77892. $used_functions = array();
  77893. $extends = array();
  77894. $implements = array();
  77895. $nodeps = array();
  77896. $inquote = false;
  77897. $interface = false;
  77898. for ($i = 0; $i < sizeof($tokens); $i++) {
  77899. if (is_array($tokens[$i])) {
  77900. list($token, $data) = $tokens[$i];
  77901. } else {
  77902. $token = $tokens[$i];
  77903. $data = '';
  77904. }
  77905. if ($inquote) {
  77906. if ($token != '"' && $token != T_END_HEREDOC) {
  77907. continue;
  77908. } else {
  77909. $inquote = false;
  77910. continue;
  77911. }
  77912. }
  77913. switch ($token) {
  77914. case T_WHITESPACE:
  77915. break;
  77916. case ';':
  77917. if ($interface) {
  77918. $current_function = '';
  77919. $current_function_level = -1;
  77920. }
  77921. break;
  77922. case '"':
  77923. case T_START_HEREDOC:
  77924. $inquote = true;
  77925. break;
  77926. case T_CURLY_OPEN:
  77927. case T_DOLLAR_OPEN_CURLY_BRACES:
  77928. case '{': $brace_level++; continue 2;
  77929. case '}':
  77930. $brace_level--;
  77931. if ($current_class_level == $brace_level) {
  77932. $current_class = '';
  77933. $current_class_level = -1;
  77934. }
  77935. if ($current_function_level == $brace_level) {
  77936. $current_function = '';
  77937. $current_function_level = -1;
  77938. }
  77939. continue 2;
  77940. case '[': $bracket_level++; continue 2;
  77941. case ']': $bracket_level--; continue 2;
  77942. case '(': $paren_level++; continue 2;
  77943. case ')': $paren_level--; continue 2;
  77944. case T_INTERFACE:
  77945. $interface = true;
  77946. case T_CLASS:
  77947. if (($current_class_level != -1) || ($current_function_level != -1)) {
  77948. $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE,
  77949. array('file' => $file));
  77950. return false;
  77951. }
  77952. case T_FUNCTION:
  77953. case T_NEW:
  77954. case T_EXTENDS:
  77955. case T_IMPLEMENTS:
  77956. $look_for = $token;
  77957. continue 2;
  77958. case T_STRING:
  77959. if ($look_for == T_CLASS) {
  77960. $current_class = $data;
  77961. $current_class_level = $brace_level;
  77962. $declared_classes[] = $current_class;
  77963. } elseif ($look_for == T_INTERFACE) {
  77964. $current_interface = $data;
  77965. $current_class_level = $brace_level;
  77966. $declared_interfaces[] = $current_interface;
  77967. } elseif ($look_for == T_IMPLEMENTS) {
  77968. $implements[$current_class] = $data;
  77969. } elseif ($look_for == T_EXTENDS) {
  77970. $extends[$current_class] = $data;
  77971. } elseif ($look_for == T_FUNCTION) {
  77972. if ($current_class) {
  77973. $current_function = "$current_class::$data";
  77974. $declared_methods[$current_class][] = $data;
  77975. } elseif ($current_interface) {
  77976. $current_function = "$current_interface::$data";
  77977. $declared_methods[$current_interface][] = $data;
  77978. } else {
  77979. $current_function = $data;
  77980. $declared_functions[] = $current_function;
  77981. }
  77982. $current_function_level = $brace_level;
  77983. $m = array();
  77984. } elseif ($look_for == T_NEW) {
  77985. $used_classes[$data] = true;
  77986. }
  77987. $look_for = 0;
  77988. continue 2;
  77989. case T_VARIABLE:
  77990. $look_for = 0;
  77991. continue 2;
  77992. case T_DOC_COMMENT:
  77993. case T_COMMENT:
  77994. if (preg_match('!^/\*\*\s!', $data)) {
  77995. $lastphpdoc = $data;
  77996. if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
  77997. $nodeps = array_merge($nodeps, $m[1]);
  77998. }
  77999. }
  78000. continue 2;
  78001. case T_DOUBLE_COLON:
  78002. if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
  78003. $this->_validateError(PEAR_PACKAGEFILE_ERROR_INVALID_PHPFILE,
  78004. array('file' => $file));
  78005. return false;
  78006. }
  78007. $class = $tokens[$i - 1][1];
  78008. if (strtolower($class) != 'parent') {
  78009. $used_classes[$class] = true;
  78010. }
  78011. continue 2;
  78012. }
  78013. }
  78014. return array(
  78015. "source_file" => $file,
  78016. "declared_classes" => $declared_classes,
  78017. "declared_interfaces" => $declared_interfaces,
  78018. "declared_methods" => $declared_methods,
  78019. "declared_functions" => $declared_functions,
  78020. "used_classes" => array_diff(array_keys($used_classes), $nodeps),
  78021. "inheritance" => $extends,
  78022. "implements" => $implements,
  78023. );
  78024. }
  78025. /**
  78026. * Build a "provides" array from data returned by
  78027. * analyzeSourceCode(). The format of the built array is like
  78028. * this:
  78029. *
  78030. * array(
  78031. * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
  78032. * ...
  78033. * )
  78034. *
  78035. *
  78036. * @param array $srcinfo array with information about a source file
  78037. * as returned by the analyzeSourceCode() method.
  78038. *
  78039. * @return void
  78040. *
  78041. * @access private
  78042. *
  78043. */
  78044. function _buildProvidesArray($srcinfo)
  78045. {
  78046. if (!$this->_isValid) {
  78047. return false;
  78048. }
  78049. $file = basename($srcinfo['source_file']);
  78050. $pn = $this->getPackage();
  78051. $pnl = strlen($pn);
  78052. foreach ($srcinfo['declared_classes'] as $class) {
  78053. $key = "class;$class";
  78054. if (isset($this->_packageInfo['provides'][$key])) {
  78055. continue;
  78056. }
  78057. $this->_packageInfo['provides'][$key] =
  78058. array('file'=> $file, 'type' => 'class', 'name' => $class);
  78059. if (isset($srcinfo['inheritance'][$class])) {
  78060. $this->_packageInfo['provides'][$key]['extends'] =
  78061. $srcinfo['inheritance'][$class];
  78062. }
  78063. }
  78064. foreach ($srcinfo['declared_methods'] as $class => $methods) {
  78065. foreach ($methods as $method) {
  78066. $function = "$class::$method";
  78067. $key = "function;$function";
  78068. if ($method[0] == '_' || !strcasecmp($method, $class) ||
  78069. isset($this->_packageInfo['provides'][$key])) {
  78070. continue;
  78071. }
  78072. $this->_packageInfo['provides'][$key] =
  78073. array('file'=> $file, 'type' => 'function', 'name' => $function);
  78074. }
  78075. }
  78076. foreach ($srcinfo['declared_functions'] as $function) {
  78077. $key = "function;$function";
  78078. if ($function[0] == '_' || isset($this->_packageInfo['provides'][$key])) {
  78079. continue;
  78080. }
  78081. if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
  78082. $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
  78083. }
  78084. $this->_packageInfo['provides'][$key] =
  78085. array('file'=> $file, 'type' => 'function', 'name' => $function);
  78086. }
  78087. }
  78088. // }}}
  78089. }
  78090. ?>
  78091. <?php
  78092. /**
  78093. * PEAR_PackageFile_v2, package.xml version 2.0
  78094. *
  78095. * PHP versions 4 and 5
  78096. *
  78097. * @category pear
  78098. * @package PEAR
  78099. * @author Greg Beaver <cellog@php.net>
  78100. * @copyright 1997-2009 The Authors
  78101. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  78102. * @link http://pear.php.net/package/PEAR
  78103. * @since File available since Release 1.4.0a1
  78104. */
  78105. /**
  78106. * For error handling
  78107. */
  78108. require_once 'phar://go-pear.phar/' . 'PEAR/ErrorStack.php';
  78109. /**
  78110. * @category pear
  78111. * @package PEAR
  78112. * @author Greg Beaver <cellog@php.net>
  78113. * @copyright 1997-2009 The Authors
  78114. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  78115. * @version Release: 1.10.16
  78116. * @link http://pear.php.net/package/PEAR
  78117. * @since Class available since Release 1.4.0a1
  78118. */
  78119. class PEAR_PackageFile_v2
  78120. {
  78121. /**
  78122. * Parsed package information
  78123. * @var array
  78124. * @access private
  78125. */
  78126. var $_packageInfo = array();
  78127. /**
  78128. * path to package .tgz or false if this is a local/extracted package.xml
  78129. * @var string|false
  78130. * @access private
  78131. */
  78132. var $_archiveFile;
  78133. /**
  78134. * path to package .xml or false if this is an abstract parsed-from-string xml
  78135. * @var string|false
  78136. * @access private
  78137. */
  78138. var $_packageFile;
  78139. /**
  78140. * This is used by file analysis routines to log progress information
  78141. * @var PEAR_Common
  78142. * @access protected
  78143. */
  78144. var $_logger;
  78145. /**
  78146. * This is set to the highest validation level that has been validated
  78147. *
  78148. * If the package.xml is invalid or unknown, this is set to 0. If
  78149. * normal validation has occurred, this is set to PEAR_VALIDATE_NORMAL. If
  78150. * downloading/installation validation has occurred it is set to PEAR_VALIDATE_DOWNLOADING
  78151. * or INSTALLING, and so on up to PEAR_VALIDATE_PACKAGING. This allows validation
  78152. * "caching" to occur, which is particularly important for package validation, so
  78153. * that PHP files are not validated twice
  78154. * @var int
  78155. * @access private
  78156. */
  78157. var $_isValid = 0;
  78158. /**
  78159. * True if the filelist has been validated
  78160. * @param bool
  78161. */
  78162. var $_filesValid = false;
  78163. /**
  78164. * @var PEAR_Registry
  78165. * @access protected
  78166. */
  78167. var $_registry;
  78168. /**
  78169. * @var PEAR_Config
  78170. * @access protected
  78171. */
  78172. var $_config;
  78173. /**
  78174. * Optional Dependency group requested for installation
  78175. * @var string
  78176. * @access private
  78177. */
  78178. var $_requestedGroup = false;
  78179. /**
  78180. * @var PEAR_ErrorStack
  78181. * @access protected
  78182. */
  78183. var $_stack;
  78184. /**
  78185. * Namespace prefix used for tasks in this package.xml - use tasks: whenever possible
  78186. */
  78187. var $_tasksNs;
  78188. /**
  78189. * Determines whether this packagefile was initialized only with partial package info
  78190. *
  78191. * If this package file was constructed via parsing REST, it will only contain
  78192. *
  78193. * - package name
  78194. * - channel name
  78195. * - dependencies
  78196. * @var boolean
  78197. * @access private
  78198. */
  78199. var $_incomplete = true;
  78200. /**
  78201. * @var PEAR_PackageFile_v2_Validator
  78202. */
  78203. var $_v2Validator;
  78204. /**
  78205. * The constructor merely sets up the private error stack
  78206. */
  78207. function __construct()
  78208. {
  78209. $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v2', false, null);
  78210. $this->_isValid = false;
  78211. }
  78212. /**
  78213. * PHP 4 style constructor for backwards compatibility.
  78214. * Used by PEAR_PackageFileManager2
  78215. */
  78216. public function PEAR_PackageFile_v2()
  78217. {
  78218. $this->__construct();
  78219. }
  78220. /**
  78221. * To make unit-testing easier
  78222. * @param PEAR_Frontend_*
  78223. * @param array options
  78224. * @param PEAR_Config
  78225. * @return PEAR_Downloader
  78226. * @access protected
  78227. */
  78228. function &getPEARDownloader(&$i, $o, &$c)
  78229. {
  78230. $z = new PEAR_Downloader($i, $o, $c);
  78231. return $z;
  78232. }
  78233. /**
  78234. * To make unit-testing easier
  78235. * @param PEAR_Config
  78236. * @param array options
  78237. * @param array package name as returned from {@link PEAR_Registry::parsePackageName()}
  78238. * @param int PEAR_VALIDATE_* constant
  78239. * @return PEAR_Dependency2
  78240. * @access protected
  78241. */
  78242. function &getPEARDependency2(&$c, $o, $p, $s = PEAR_VALIDATE_INSTALLING)
  78243. {
  78244. if (!class_exists('PEAR_Dependency2')) {
  78245. require_once 'phar://go-pear.phar/' . 'PEAR/Dependency2.php';
  78246. }
  78247. $z = new PEAR_Dependency2($c, $o, $p, $s);
  78248. return $z;
  78249. }
  78250. function getInstalledBinary()
  78251. {
  78252. return isset($this->_packageInfo['#binarypackage']) ? $this->_packageInfo['#binarypackage'] :
  78253. false;
  78254. }
  78255. /**
  78256. * Installation of source package has failed, attempt to download and install the
  78257. * binary version of this package.
  78258. * @param PEAR_Installer
  78259. * @return array|false
  78260. */
  78261. function installBinary(&$installer)
  78262. {
  78263. if (!OS_WINDOWS) {
  78264. $a = false;
  78265. return $a;
  78266. }
  78267. if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
  78268. $releasetype = $this->getPackageType() . 'release';
  78269. if (!is_array($installer->getInstallPackages())) {
  78270. $a = false;
  78271. return $a;
  78272. }
  78273. foreach ($installer->getInstallPackages() as $p) {
  78274. if ($p->isExtension($this->_packageInfo['providesextension'])) {
  78275. if ($p->getPackageType() != 'extsrc' && $p->getPackageType() != 'zendextsrc') {
  78276. $a = false;
  78277. return $a; // the user probably downloaded it separately
  78278. }
  78279. }
  78280. }
  78281. if (isset($this->_packageInfo[$releasetype]['binarypackage'])) {
  78282. $installer->log(0, 'Attempting to download binary version of extension "' .
  78283. $this->_packageInfo['providesextension'] . '"');
  78284. $params = $this->_packageInfo[$releasetype]['binarypackage'];
  78285. if (!is_array($params) || !isset($params[0])) {
  78286. $params = array($params);
  78287. }
  78288. if (isset($this->_packageInfo['channel'])) {
  78289. foreach ($params as $i => $param) {
  78290. $params[$i] = array('channel' => $this->_packageInfo['channel'],
  78291. 'package' => $param, 'version' => $this->getVersion());
  78292. }
  78293. }
  78294. $dl = &$this->getPEARDownloader($installer->ui, $installer->getOptions(),
  78295. $installer->config);
  78296. $verbose = $dl->config->get('verbose');
  78297. $dl->config->set('verbose', -1);
  78298. foreach ($params as $param) {
  78299. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  78300. $ret = $dl->download(array($param));
  78301. PEAR::popErrorHandling();
  78302. if (is_array($ret) && count($ret)) {
  78303. break;
  78304. }
  78305. }
  78306. $dl->config->set('verbose', $verbose);
  78307. if (is_array($ret)) {
  78308. if (count($ret) == 1) {
  78309. $pf = $ret[0]->getPackageFile();
  78310. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  78311. $err = $installer->install($ret[0]);
  78312. PEAR::popErrorHandling();
  78313. if (is_array($err)) {
  78314. $this->_packageInfo['#binarypackage'] = $ret[0]->getPackage();
  78315. // "install" self, so all dependencies will work transparently
  78316. $this->_registry->addPackage2($this);
  78317. $installer->log(0, 'Download and install of binary extension "' .
  78318. $this->_registry->parsedPackageNameToString(
  78319. array('channel' => $pf->getChannel(),
  78320. 'package' => $pf->getPackage()), true) . '" successful');
  78321. $a = array($ret[0], $err);
  78322. return $a;
  78323. }
  78324. $installer->log(0, 'Download and install of binary extension "' .
  78325. $this->_registry->parsedPackageNameToString(
  78326. array('channel' => $pf->getChannel(),
  78327. 'package' => $pf->getPackage()), true) . '" failed');
  78328. }
  78329. }
  78330. }
  78331. }
  78332. $a = false;
  78333. return $a;
  78334. }
  78335. /**
  78336. * @return string|false Extension name
  78337. */
  78338. function getProvidesExtension()
  78339. {
  78340. if (in_array($this->getPackageType(),
  78341. array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
  78342. if (isset($this->_packageInfo['providesextension'])) {
  78343. return $this->_packageInfo['providesextension'];
  78344. }
  78345. }
  78346. return false;
  78347. }
  78348. /**
  78349. * @param string Extension name
  78350. * @return bool
  78351. */
  78352. function isExtension($extension)
  78353. {
  78354. if (in_array($this->getPackageType(),
  78355. array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
  78356. return $this->_packageInfo['providesextension'] == $extension;
  78357. }
  78358. return false;
  78359. }
  78360. /**
  78361. * Tests whether every part of the package.xml 1.0 is represented in
  78362. * this package.xml 2.0
  78363. * @param PEAR_PackageFile_v1
  78364. * @return bool
  78365. */
  78366. function isEquivalent($pf1)
  78367. {
  78368. if (!$pf1) {
  78369. return true;
  78370. }
  78371. if ($this->getPackageType() == 'bundle') {
  78372. return false;
  78373. }
  78374. $this->_stack->getErrors(true);
  78375. if (!$pf1->validate(PEAR_VALIDATE_NORMAL)) {
  78376. return false;
  78377. }
  78378. $pass = true;
  78379. if ($pf1->getPackage() != $this->getPackage()) {
  78380. $this->_differentPackage($pf1->getPackage());
  78381. $pass = false;
  78382. }
  78383. if ($pf1->getVersion() != $this->getVersion()) {
  78384. $this->_differentVersion($pf1->getVersion());
  78385. $pass = false;
  78386. }
  78387. if (trim($pf1->getSummary()) != $this->getSummary()) {
  78388. $this->_differentSummary($pf1->getSummary());
  78389. $pass = false;
  78390. }
  78391. if (preg_replace('/\s+/', '', $pf1->getDescription()) !=
  78392. preg_replace('/\s+/', '', $this->getDescription())) {
  78393. $this->_differentDescription($pf1->getDescription());
  78394. $pass = false;
  78395. }
  78396. if ($pf1->getState() != $this->getState()) {
  78397. $this->_differentState($pf1->getState());
  78398. $pass = false;
  78399. }
  78400. if (!strstr(preg_replace('/\s+/', '', $this->getNotes()),
  78401. preg_replace('/\s+/', '', $pf1->getNotes()))) {
  78402. $this->_differentNotes($pf1->getNotes());
  78403. $pass = false;
  78404. }
  78405. $mymaintainers = $this->getMaintainers();
  78406. $yourmaintainers = $pf1->getMaintainers();
  78407. for ($i1 = 0; $i1 < count($yourmaintainers); $i1++) {
  78408. $reset = false;
  78409. for ($i2 = 0; $i2 < count($mymaintainers); $i2++) {
  78410. if ($mymaintainers[$i2]['handle'] == $yourmaintainers[$i1]['handle']) {
  78411. if ($mymaintainers[$i2]['role'] != $yourmaintainers[$i1]['role']) {
  78412. $this->_differentRole($mymaintainers[$i2]['handle'],
  78413. $yourmaintainers[$i1]['role'], $mymaintainers[$i2]['role']);
  78414. $pass = false;
  78415. }
  78416. if ($mymaintainers[$i2]['email'] != $yourmaintainers[$i1]['email']) {
  78417. $this->_differentEmail($mymaintainers[$i2]['handle'],
  78418. $yourmaintainers[$i1]['email'], $mymaintainers[$i2]['email']);
  78419. $pass = false;
  78420. }
  78421. if ($mymaintainers[$i2]['name'] != $yourmaintainers[$i1]['name']) {
  78422. $this->_differentName($mymaintainers[$i2]['handle'],
  78423. $yourmaintainers[$i1]['name'], $mymaintainers[$i2]['name']);
  78424. $pass = false;
  78425. }
  78426. unset($mymaintainers[$i2]);
  78427. $mymaintainers = array_values($mymaintainers);
  78428. unset($yourmaintainers[$i1]);
  78429. $yourmaintainers = array_values($yourmaintainers);
  78430. $reset = true;
  78431. break;
  78432. }
  78433. }
  78434. if ($reset) {
  78435. $i1 = -1;
  78436. }
  78437. }
  78438. $this->_unmatchedMaintainers($mymaintainers, $yourmaintainers);
  78439. $filelist = $this->getFilelist();
  78440. foreach ($pf1->getFilelist() as $file => $atts) {
  78441. if (!isset($filelist[$file])) {
  78442. $this->_missingFile($file);
  78443. $pass = false;
  78444. }
  78445. }
  78446. return $pass;
  78447. }
  78448. function _differentPackage($package)
  78449. {
  78450. $this->_stack->push(__FUNCTION__, 'error', array('package' => $package,
  78451. 'self' => $this->getPackage()),
  78452. 'package.xml 1.0 package "%package%" does not match "%self%"');
  78453. }
  78454. function _differentVersion($version)
  78455. {
  78456. $this->_stack->push(__FUNCTION__, 'error', array('version' => $version,
  78457. 'self' => $this->getVersion()),
  78458. 'package.xml 1.0 version "%version%" does not match "%self%"');
  78459. }
  78460. function _differentState($state)
  78461. {
  78462. $this->_stack->push(__FUNCTION__, 'error', array('state' => $state,
  78463. 'self' => $this->getState()),
  78464. 'package.xml 1.0 state "%state%" does not match "%self%"');
  78465. }
  78466. function _differentRole($handle, $role, $selfrole)
  78467. {
  78468. $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
  78469. 'role' => $role, 'self' => $selfrole),
  78470. 'package.xml 1.0 maintainer "%handle%" role "%role%" does not match "%self%"');
  78471. }
  78472. function _differentEmail($handle, $email, $selfemail)
  78473. {
  78474. $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
  78475. 'email' => $email, 'self' => $selfemail),
  78476. 'package.xml 1.0 maintainer "%handle%" email "%email%" does not match "%self%"');
  78477. }
  78478. function _differentName($handle, $name, $selfname)
  78479. {
  78480. $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
  78481. 'name' => $name, 'self' => $selfname),
  78482. 'package.xml 1.0 maintainer "%handle%" name "%name%" does not match "%self%"');
  78483. }
  78484. function _unmatchedMaintainers($my, $yours)
  78485. {
  78486. if ($my) {
  78487. array_walk($my, function(&$i, $k) { $i = $i["handle"]; });
  78488. $this->_stack->push(__FUNCTION__, 'error', array('handles' => $my),
  78489. 'package.xml 2.0 has unmatched extra maintainers "%handles%"');
  78490. }
  78491. if ($yours) {
  78492. array_walk($yours, function(&$i, $k) { $i = $i["handle"]; });
  78493. $this->_stack->push(__FUNCTION__, 'error', array('handles' => $yours),
  78494. 'package.xml 1.0 has unmatched extra maintainers "%handles%"');
  78495. }
  78496. }
  78497. function _differentNotes($notes)
  78498. {
  78499. $truncnotes = strlen($notes) < 25 ? $notes : substr($notes, 0, 24) . '...';
  78500. $truncmynotes = strlen($this->getNotes()) < 25 ? $this->getNotes() :
  78501. substr($this->getNotes(), 0, 24) . '...';
  78502. $this->_stack->push(__FUNCTION__, 'error', array('notes' => $truncnotes,
  78503. 'self' => $truncmynotes),
  78504. 'package.xml 1.0 release notes "%notes%" do not match "%self%"');
  78505. }
  78506. function _differentSummary($summary)
  78507. {
  78508. $truncsummary = strlen($summary) < 25 ? $summary : substr($summary, 0, 24) . '...';
  78509. $truncmysummary = strlen($this->getsummary()) < 25 ? $this->getSummary() :
  78510. substr($this->getsummary(), 0, 24) . '...';
  78511. $this->_stack->push(__FUNCTION__, 'error', array('summary' => $truncsummary,
  78512. 'self' => $truncmysummary),
  78513. 'package.xml 1.0 summary "%summary%" does not match "%self%"');
  78514. }
  78515. function _differentDescription($description)
  78516. {
  78517. $truncdescription = trim(strlen($description) < 25 ? $description : substr($description, 0, 24) . '...');
  78518. $truncmydescription = trim(strlen($this->getDescription()) < 25 ? $this->getDescription() :
  78519. substr($this->getdescription(), 0, 24) . '...');
  78520. $this->_stack->push(__FUNCTION__, 'error', array('description' => $truncdescription,
  78521. 'self' => $truncmydescription),
  78522. 'package.xml 1.0 description "%description%" does not match "%self%"');
  78523. }
  78524. function _missingFile($file)
  78525. {
  78526. $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
  78527. 'package.xml 1.0 file "%file%" is not present in <contents>');
  78528. }
  78529. /**
  78530. * WARNING - do not use this function unless you know what you're doing
  78531. */
  78532. function setRawState($state)
  78533. {
  78534. if (!isset($this->_packageInfo['stability'])) {
  78535. $this->_packageInfo['stability'] = array();
  78536. }
  78537. $this->_packageInfo['stability']['release'] = $state;
  78538. }
  78539. /**
  78540. * WARNING - do not use this function unless you know what you're doing
  78541. */
  78542. function setRawCompatible($compatible)
  78543. {
  78544. $this->_packageInfo['compatible'] = $compatible;
  78545. }
  78546. /**
  78547. * WARNING - do not use this function unless you know what you're doing
  78548. */
  78549. function setRawPackage($package)
  78550. {
  78551. $this->_packageInfo['name'] = $package;
  78552. }
  78553. /**
  78554. * WARNING - do not use this function unless you know what you're doing
  78555. */
  78556. function setRawChannel($channel)
  78557. {
  78558. $this->_packageInfo['channel'] = $channel;
  78559. }
  78560. function setRequestedGroup($group)
  78561. {
  78562. $this->_requestedGroup = $group;
  78563. }
  78564. function getRequestedGroup()
  78565. {
  78566. if (isset($this->_requestedGroup)) {
  78567. return $this->_requestedGroup;
  78568. }
  78569. return false;
  78570. }
  78571. /**
  78572. * For saving in the registry.
  78573. *
  78574. * Set the last version that was installed
  78575. * @param string
  78576. */
  78577. function setLastInstalledVersion($version)
  78578. {
  78579. $this->_packageInfo['_lastversion'] = $version;
  78580. }
  78581. /**
  78582. * @return string|false
  78583. */
  78584. function getLastInstalledVersion()
  78585. {
  78586. if (isset($this->_packageInfo['_lastversion'])) {
  78587. return $this->_packageInfo['_lastversion'];
  78588. }
  78589. return false;
  78590. }
  78591. /**
  78592. * Determines whether this package.xml has post-install scripts or not
  78593. * @return array|false
  78594. */
  78595. function listPostinstallScripts()
  78596. {
  78597. $filelist = $this->getFilelist();
  78598. $contents = $this->getContents();
  78599. $contents = $contents['dir']['file'];
  78600. if (!is_array($contents) || !isset($contents[0])) {
  78601. $contents = array($contents);
  78602. }
  78603. $taskfiles = array();
  78604. foreach ($contents as $file) {
  78605. $atts = $file['attribs'];
  78606. unset($file['attribs']);
  78607. if (count($file)) {
  78608. $taskfiles[$atts['name']] = $file;
  78609. }
  78610. }
  78611. $common = new PEAR_Common;
  78612. $common->debug = $this->_config->get('verbose');
  78613. $this->_scripts = array();
  78614. $ret = array();
  78615. foreach ($taskfiles as $name => $tasks) {
  78616. if (!isset($filelist[$name])) {
  78617. // ignored files will not be in the filelist
  78618. continue;
  78619. }
  78620. $atts = $filelist[$name];
  78621. foreach ($tasks as $tag => $raw) {
  78622. $task = $this->getTask($tag);
  78623. $task = new $task($this->_config, $common, PEAR_TASK_INSTALL);
  78624. if ($task->isScript()) {
  78625. $ret[] = $filelist[$name]['installed_as'];
  78626. }
  78627. }
  78628. }
  78629. if (count($ret)) {
  78630. return $ret;
  78631. }
  78632. return false;
  78633. }
  78634. /**
  78635. * Initialize post-install scripts for running
  78636. *
  78637. * This method can be used to detect post-install scripts, as the return value
  78638. * indicates whether any exist
  78639. * @return bool
  78640. */
  78641. function initPostinstallScripts()
  78642. {
  78643. $filelist = $this->getFilelist();
  78644. $contents = $this->getContents();
  78645. $contents = $contents['dir']['file'];
  78646. if (!is_array($contents) || !isset($contents[0])) {
  78647. $contents = array($contents);
  78648. }
  78649. $taskfiles = array();
  78650. foreach ($contents as $file) {
  78651. $atts = $file['attribs'];
  78652. unset($file['attribs']);
  78653. if (count($file)) {
  78654. $taskfiles[$atts['name']] = $file;
  78655. }
  78656. }
  78657. $common = new PEAR_Common;
  78658. $common->debug = $this->_config->get('verbose');
  78659. $this->_scripts = array();
  78660. foreach ($taskfiles as $name => $tasks) {
  78661. if (!isset($filelist[$name])) {
  78662. // file was not installed due to installconditions
  78663. continue;
  78664. }
  78665. $atts = $filelist[$name];
  78666. foreach ($tasks as $tag => $raw) {
  78667. $taskname = $this->getTask($tag);
  78668. $task = new $taskname($this->_config, $common, PEAR_TASK_INSTALL);
  78669. if (!$task->isScript()) {
  78670. continue; // scripts are only handled after installation
  78671. }
  78672. $lastversion = isset($this->_packageInfo['_lastversion']) ?
  78673. $this->_packageInfo['_lastversion'] : null;
  78674. $task->init($raw, $atts, $lastversion);
  78675. $res = $task->startSession($this, $atts['installed_as'], null);
  78676. if (!$res) {
  78677. continue; // skip this file
  78678. }
  78679. if (PEAR::isError($res)) {
  78680. return $res;
  78681. }
  78682. $this->_scripts[] = $task;
  78683. }
  78684. }
  78685. if (count($this->_scripts)) {
  78686. return true;
  78687. }
  78688. return false;
  78689. }
  78690. function runPostinstallScripts()
  78691. {
  78692. if ($this->initPostinstallScripts()) {
  78693. $ui = &PEAR_Frontend::singleton();
  78694. if ($ui) {
  78695. $ui->runPostinstallScripts($this->_scripts, $this);
  78696. }
  78697. }
  78698. }
  78699. /**
  78700. * Convert a recursive set of <dir> and <file> tags into a single <dir> tag with
  78701. * <file> tags.
  78702. */
  78703. function flattenFilelist()
  78704. {
  78705. if (isset($this->_packageInfo['bundle'])) {
  78706. return;
  78707. }
  78708. $filelist = array();
  78709. if (isset($this->_packageInfo['contents']['dir']['dir'])) {
  78710. $this->_getFlattenedFilelist($filelist, $this->_packageInfo['contents']['dir']);
  78711. if (!isset($filelist[1])) {
  78712. $filelist = $filelist[0];
  78713. }
  78714. $this->_packageInfo['contents']['dir']['file'] = $filelist;
  78715. unset($this->_packageInfo['contents']['dir']['dir']);
  78716. } else {
  78717. // else already flattened but check for baseinstalldir propagation
  78718. if (isset($this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'])) {
  78719. if (isset($this->_packageInfo['contents']['dir']['file'][0])) {
  78720. foreach ($this->_packageInfo['contents']['dir']['file'] as $i => $file) {
  78721. if (isset($file['attribs']['baseinstalldir'])) {
  78722. continue;
  78723. }
  78724. $this->_packageInfo['contents']['dir']['file'][$i]['attribs']['baseinstalldir']
  78725. = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
  78726. }
  78727. } else {
  78728. if (!isset($this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'])) {
  78729. $this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir']
  78730. = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
  78731. }
  78732. }
  78733. }
  78734. }
  78735. }
  78736. /**
  78737. * @param array the final flattened file list
  78738. * @param array the current directory being processed
  78739. * @param string|false any recursively inherited baeinstalldir attribute
  78740. * @param string private recursion variable
  78741. * @return array
  78742. * @access protected
  78743. */
  78744. function _getFlattenedFilelist(&$files, $dir, $baseinstall = false, $path = '')
  78745. {
  78746. if (isset($dir['attribs']) && isset($dir['attribs']['baseinstalldir'])) {
  78747. $baseinstall = $dir['attribs']['baseinstalldir'];
  78748. }
  78749. if (isset($dir['dir'])) {
  78750. if (!isset($dir['dir'][0])) {
  78751. $dir['dir'] = array($dir['dir']);
  78752. }
  78753. foreach ($dir['dir'] as $subdir) {
  78754. if (!isset($subdir['attribs']) || !isset($subdir['attribs']['name'])) {
  78755. $name = '*unknown*';
  78756. } else {
  78757. $name = $subdir['attribs']['name'];
  78758. }
  78759. $newpath = empty($path) ? $name :
  78760. $path . '/' . $name;
  78761. $this->_getFlattenedFilelist($files, $subdir,
  78762. $baseinstall, $newpath);
  78763. }
  78764. }
  78765. if (isset($dir['file'])) {
  78766. if (!isset($dir['file'][0])) {
  78767. $dir['file'] = array($dir['file']);
  78768. }
  78769. foreach ($dir['file'] as $file) {
  78770. $attrs = $file['attribs'];
  78771. $name = $attrs['name'];
  78772. if ($baseinstall && !isset($attrs['baseinstalldir'])) {
  78773. $attrs['baseinstalldir'] = $baseinstall;
  78774. }
  78775. $attrs['name'] = empty($path) ? $name : $path . '/' . $name;
  78776. $attrs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
  78777. $attrs['name']);
  78778. $file['attribs'] = $attrs;
  78779. $files[] = $file;
  78780. }
  78781. }
  78782. }
  78783. function setConfig(&$config)
  78784. {
  78785. $this->_config = &$config;
  78786. $this->_registry = &$config->getRegistry();
  78787. }
  78788. function setLogger(&$logger)
  78789. {
  78790. if (!is_object($logger) || !method_exists($logger, 'log')) {
  78791. return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
  78792. }
  78793. $this->_logger = &$logger;
  78794. }
  78795. /**
  78796. * WARNING - do not use this function directly unless you know what you're doing
  78797. */
  78798. function setDeps($deps)
  78799. {
  78800. $this->_packageInfo['dependencies'] = $deps;
  78801. }
  78802. /**
  78803. * WARNING - do not use this function directly unless you know what you're doing
  78804. */
  78805. function setCompatible($compat)
  78806. {
  78807. $this->_packageInfo['compatible'] = $compat;
  78808. }
  78809. function setPackagefile($file, $archive = false)
  78810. {
  78811. $this->_packageFile = $file;
  78812. $this->_archiveFile = $archive ? $archive : $file;
  78813. }
  78814. /**
  78815. * Wrapper to {@link PEAR_ErrorStack::getErrors()}
  78816. * @param boolean determines whether to purge the error stack after retrieving
  78817. * @return array
  78818. */
  78819. function getValidationWarnings($purge = true)
  78820. {
  78821. return $this->_stack->getErrors($purge);
  78822. }
  78823. function getPackageFile()
  78824. {
  78825. return $this->_packageFile;
  78826. }
  78827. function getArchiveFile()
  78828. {
  78829. return $this->_archiveFile;
  78830. }
  78831. /**
  78832. * Directly set the array that defines this packagefile
  78833. *
  78834. * WARNING: no validation. This should only be performed by internal methods
  78835. * inside PEAR or by inputting an array saved from an existing PEAR_PackageFile_v2
  78836. * @param array
  78837. */
  78838. function fromArray($pinfo)
  78839. {
  78840. unset($pinfo['old']);
  78841. unset($pinfo['xsdversion']);
  78842. // If the changelog isn't an array then it was passed in as an empty tag
  78843. if (isset($pinfo['changelog']) && !is_array($pinfo['changelog'])) {
  78844. unset($pinfo['changelog']);
  78845. }
  78846. $this->_incomplete = false;
  78847. $this->_packageInfo = $pinfo;
  78848. }
  78849. function isIncomplete()
  78850. {
  78851. return $this->_incomplete;
  78852. }
  78853. /**
  78854. * @return array
  78855. */
  78856. function toArray($forreg = false)
  78857. {
  78858. if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
  78859. return false;
  78860. }
  78861. return $this->getArray($forreg);
  78862. }
  78863. function getArray($forReg = false)
  78864. {
  78865. if ($forReg) {
  78866. $arr = $this->_packageInfo;
  78867. $arr['old'] = array();
  78868. $arr['old']['version'] = $this->getVersion();
  78869. $arr['old']['release_date'] = $this->getDate();
  78870. $arr['old']['release_state'] = $this->getState();
  78871. $arr['old']['release_license'] = $this->getLicense();
  78872. $arr['old']['release_notes'] = $this->getNotes();
  78873. $arr['old']['release_deps'] = $this->getDeps();
  78874. $arr['old']['maintainers'] = $this->getMaintainers();
  78875. $arr['xsdversion'] = '2.0';
  78876. return $arr;
  78877. } else {
  78878. $info = $this->_packageInfo;
  78879. unset($info['dirtree']);
  78880. if (isset($info['_lastversion'])) {
  78881. unset($info['_lastversion']);
  78882. }
  78883. if (isset($info['#binarypackage'])) {
  78884. unset($info['#binarypackage']);
  78885. }
  78886. return $info;
  78887. }
  78888. }
  78889. function packageInfo($field)
  78890. {
  78891. $arr = $this->getArray(true);
  78892. if ($field == 'state') {
  78893. return $arr['stability']['release'];
  78894. }
  78895. if ($field == 'api-version') {
  78896. return $arr['version']['api'];
  78897. }
  78898. if ($field == 'api-state') {
  78899. return $arr['stability']['api'];
  78900. }
  78901. if (isset($arr['old'][$field])) {
  78902. if (!is_string($arr['old'][$field])) {
  78903. return null;
  78904. }
  78905. return $arr['old'][$field];
  78906. }
  78907. if (isset($arr[$field])) {
  78908. if (!is_string($arr[$field])) {
  78909. return null;
  78910. }
  78911. return $arr[$field];
  78912. }
  78913. return null;
  78914. }
  78915. function getName()
  78916. {
  78917. return $this->getPackage();
  78918. }
  78919. function getPackage()
  78920. {
  78921. if (isset($this->_packageInfo['name'])) {
  78922. return $this->_packageInfo['name'];
  78923. }
  78924. return false;
  78925. }
  78926. function getChannel()
  78927. {
  78928. if (isset($this->_packageInfo['uri'])) {
  78929. return '__uri';
  78930. }
  78931. if (isset($this->_packageInfo['channel'])) {
  78932. return strtolower($this->_packageInfo['channel']);
  78933. }
  78934. return false;
  78935. }
  78936. function getUri()
  78937. {
  78938. if (isset($this->_packageInfo['uri'])) {
  78939. return $this->_packageInfo['uri'];
  78940. }
  78941. return false;
  78942. }
  78943. function getExtends()
  78944. {
  78945. if (isset($this->_packageInfo['extends'])) {
  78946. return $this->_packageInfo['extends'];
  78947. }
  78948. return false;
  78949. }
  78950. function getSummary()
  78951. {
  78952. if (isset($this->_packageInfo['summary'])) {
  78953. return $this->_packageInfo['summary'];
  78954. }
  78955. return false;
  78956. }
  78957. function getDescription()
  78958. {
  78959. if (isset($this->_packageInfo['description'])) {
  78960. return $this->_packageInfo['description'];
  78961. }
  78962. return false;
  78963. }
  78964. function getMaintainers($raw = false)
  78965. {
  78966. if (!isset($this->_packageInfo['lead'])) {
  78967. return false;
  78968. }
  78969. if ($raw) {
  78970. $ret = array('lead' => $this->_packageInfo['lead']);
  78971. (isset($this->_packageInfo['developer'])) ?
  78972. $ret['developer'] = $this->_packageInfo['developer'] :null;
  78973. (isset($this->_packageInfo['contributor'])) ?
  78974. $ret['contributor'] = $this->_packageInfo['contributor'] :null;
  78975. (isset($this->_packageInfo['helper'])) ?
  78976. $ret['helper'] = $this->_packageInfo['helper'] :null;
  78977. return $ret;
  78978. } else {
  78979. $ret = array();
  78980. $leads = isset($this->_packageInfo['lead'][0]) ? $this->_packageInfo['lead'] :
  78981. array($this->_packageInfo['lead']);
  78982. foreach ($leads as $lead) {
  78983. $s = $lead;
  78984. $s['handle'] = $s['user'];
  78985. unset($s['user']);
  78986. $s['role'] = 'lead';
  78987. $ret[] = $s;
  78988. }
  78989. if (isset($this->_packageInfo['developer'])) {
  78990. $leads = isset($this->_packageInfo['developer'][0]) ?
  78991. $this->_packageInfo['developer'] :
  78992. array($this->_packageInfo['developer']);
  78993. foreach ($leads as $maintainer) {
  78994. $s = $maintainer;
  78995. $s['handle'] = $s['user'];
  78996. unset($s['user']);
  78997. $s['role'] = 'developer';
  78998. $ret[] = $s;
  78999. }
  79000. }
  79001. if (isset($this->_packageInfo['contributor'])) {
  79002. $leads = isset($this->_packageInfo['contributor'][0]) ?
  79003. $this->_packageInfo['contributor'] :
  79004. array($this->_packageInfo['contributor']);
  79005. foreach ($leads as $maintainer) {
  79006. $s = $maintainer;
  79007. $s['handle'] = $s['user'];
  79008. unset($s['user']);
  79009. $s['role'] = 'contributor';
  79010. $ret[] = $s;
  79011. }
  79012. }
  79013. if (isset($this->_packageInfo['helper'])) {
  79014. $leads = isset($this->_packageInfo['helper'][0]) ?
  79015. $this->_packageInfo['helper'] :
  79016. array($this->_packageInfo['helper']);
  79017. foreach ($leads as $maintainer) {
  79018. $s = $maintainer;
  79019. $s['handle'] = $s['user'];
  79020. unset($s['user']);
  79021. $s['role'] = 'helper';
  79022. $ret[] = $s;
  79023. }
  79024. }
  79025. return $ret;
  79026. }
  79027. return false;
  79028. }
  79029. function getLeads()
  79030. {
  79031. if (isset($this->_packageInfo['lead'])) {
  79032. return $this->_packageInfo['lead'];
  79033. }
  79034. return false;
  79035. }
  79036. function getDevelopers()
  79037. {
  79038. if (isset($this->_packageInfo['developer'])) {
  79039. return $this->_packageInfo['developer'];
  79040. }
  79041. return false;
  79042. }
  79043. function getContributors()
  79044. {
  79045. if (isset($this->_packageInfo['contributor'])) {
  79046. return $this->_packageInfo['contributor'];
  79047. }
  79048. return false;
  79049. }
  79050. function getHelpers()
  79051. {
  79052. if (isset($this->_packageInfo['helper'])) {
  79053. return $this->_packageInfo['helper'];
  79054. }
  79055. return false;
  79056. }
  79057. function setDate($date)
  79058. {
  79059. if (!isset($this->_packageInfo['date'])) {
  79060. // ensure that the extends tag is set up in the right location
  79061. $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
  79062. array('time', 'version',
  79063. 'stability', 'license', 'notes', 'contents', 'compatible',
  79064. 'dependencies', 'providesextension', 'srcpackage', 'srcuri',
  79065. 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
  79066. 'zendextbinrelease', 'bundle', 'changelog'), array(), 'date');
  79067. }
  79068. $this->_packageInfo['date'] = $date;
  79069. $this->_isValid = 0;
  79070. }
  79071. function setTime($time)
  79072. {
  79073. $this->_isValid = 0;
  79074. if (!isset($this->_packageInfo['time'])) {
  79075. // ensure that the time tag is set up in the right location
  79076. $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
  79077. array('version',
  79078. 'stability', 'license', 'notes', 'contents', 'compatible',
  79079. 'dependencies', 'providesextension', 'srcpackage', 'srcuri',
  79080. 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
  79081. 'zendextbinrelease', 'bundle', 'changelog'), $time, 'time');
  79082. }
  79083. $this->_packageInfo['time'] = $time;
  79084. }
  79085. function getDate()
  79086. {
  79087. if (isset($this->_packageInfo['date'])) {
  79088. return $this->_packageInfo['date'];
  79089. }
  79090. return false;
  79091. }
  79092. function getTime()
  79093. {
  79094. if (isset($this->_packageInfo['time'])) {
  79095. return $this->_packageInfo['time'];
  79096. }
  79097. return false;
  79098. }
  79099. /**
  79100. * @param package|api version category to return
  79101. */
  79102. function getVersion($key = 'release')
  79103. {
  79104. if (isset($this->_packageInfo['version'][$key])) {
  79105. return $this->_packageInfo['version'][$key];
  79106. }
  79107. return false;
  79108. }
  79109. function getStability()
  79110. {
  79111. if (isset($this->_packageInfo['stability'])) {
  79112. return $this->_packageInfo['stability'];
  79113. }
  79114. return false;
  79115. }
  79116. function getState($key = 'release')
  79117. {
  79118. if (isset($this->_packageInfo['stability'][$key])) {
  79119. return $this->_packageInfo['stability'][$key];
  79120. }
  79121. return false;
  79122. }
  79123. function getLicense($raw = false)
  79124. {
  79125. if (isset($this->_packageInfo['license'])) {
  79126. if ($raw) {
  79127. return $this->_packageInfo['license'];
  79128. }
  79129. if (is_array($this->_packageInfo['license'])) {
  79130. return $this->_packageInfo['license']['_content'];
  79131. } else {
  79132. return $this->_packageInfo['license'];
  79133. }
  79134. }
  79135. return false;
  79136. }
  79137. function getLicenseLocation()
  79138. {
  79139. if (!isset($this->_packageInfo['license']) || !is_array($this->_packageInfo['license'])) {
  79140. return false;
  79141. }
  79142. return $this->_packageInfo['license']['attribs'];
  79143. }
  79144. function getNotes()
  79145. {
  79146. if (isset($this->_packageInfo['notes'])) {
  79147. return $this->_packageInfo['notes'];
  79148. }
  79149. return false;
  79150. }
  79151. /**
  79152. * Return the <usesrole> tag contents, if any
  79153. * @return array|false
  79154. */
  79155. function getUsesrole()
  79156. {
  79157. if (isset($this->_packageInfo['usesrole'])) {
  79158. return $this->_packageInfo['usesrole'];
  79159. }
  79160. return false;
  79161. }
  79162. /**
  79163. * Return the <usestask> tag contents, if any
  79164. * @return array|false
  79165. */
  79166. function getUsestask()
  79167. {
  79168. if (isset($this->_packageInfo['usestask'])) {
  79169. return $this->_packageInfo['usestask'];
  79170. }
  79171. return false;
  79172. }
  79173. /**
  79174. * This should only be used to retrieve filenames and install attributes
  79175. */
  79176. function getFilelist($preserve = false)
  79177. {
  79178. if (isset($this->_packageInfo['filelist']) && !$preserve) {
  79179. return $this->_packageInfo['filelist'];
  79180. }
  79181. $this->flattenFilelist();
  79182. if ($contents = $this->getContents()) {
  79183. $ret = array();
  79184. if (!isset($contents['dir'])) {
  79185. return false;
  79186. }
  79187. if (!isset($contents['dir']['file'][0])) {
  79188. $contents['dir']['file'] = array($contents['dir']['file']);
  79189. }
  79190. foreach ($contents['dir']['file'] as $file) {
  79191. if (!isset($file['attribs']['name'])) {
  79192. continue;
  79193. }
  79194. $name = $file['attribs']['name'];
  79195. if (!$preserve) {
  79196. $file = $file['attribs'];
  79197. }
  79198. $ret[$name] = $file;
  79199. }
  79200. if (!$preserve) {
  79201. $this->_packageInfo['filelist'] = $ret;
  79202. }
  79203. return $ret;
  79204. }
  79205. return false;
  79206. }
  79207. /**
  79208. * Return configure options array, if any
  79209. *
  79210. * @return array|false
  79211. */
  79212. function getConfigureOptions()
  79213. {
  79214. if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
  79215. return false;
  79216. }
  79217. $releases = $this->getReleases();
  79218. if (isset($releases[0])) {
  79219. $releases = $releases[0];
  79220. }
  79221. if (isset($releases['configureoption'])) {
  79222. if (!isset($releases['configureoption'][0])) {
  79223. $releases['configureoption'] = array($releases['configureoption']);
  79224. }
  79225. for ($i = 0; $i < count($releases['configureoption']); $i++) {
  79226. $releases['configureoption'][$i] = $releases['configureoption'][$i]['attribs'];
  79227. }
  79228. return $releases['configureoption'];
  79229. }
  79230. return false;
  79231. }
  79232. /**
  79233. * This is only used at install-time, after all serialization
  79234. * is over.
  79235. */
  79236. function resetFilelist()
  79237. {
  79238. $this->_packageInfo['filelist'] = array();
  79239. }
  79240. /**
  79241. * Retrieve a list of files that should be installed on this computer
  79242. * @return array
  79243. */
  79244. function getInstallationFilelist($forfilecheck = false)
  79245. {
  79246. $contents = $this->getFilelist(true);
  79247. if (isset($contents['dir']['attribs']['baseinstalldir'])) {
  79248. $base = $contents['dir']['attribs']['baseinstalldir'];
  79249. }
  79250. if (isset($this->_packageInfo['bundle'])) {
  79251. return PEAR::raiseError(
  79252. 'Exception: bundles should be handled in download code only');
  79253. }
  79254. $release = $this->getReleases();
  79255. if ($release) {
  79256. if (!isset($release[0])) {
  79257. if (!isset($release['installconditions']) && !isset($release['filelist'])) {
  79258. if ($forfilecheck) {
  79259. return $this->getFilelist();
  79260. }
  79261. return $contents;
  79262. }
  79263. $release = array($release);
  79264. }
  79265. $depchecker = &$this->getPEARDependency2($this->_config, array(),
  79266. array('channel' => $this->getChannel(), 'package' => $this->getPackage()),
  79267. PEAR_VALIDATE_INSTALLING);
  79268. foreach ($release as $instance) {
  79269. if (isset($instance['installconditions'])) {
  79270. $installconditions = $instance['installconditions'];
  79271. if (is_array($installconditions)) {
  79272. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  79273. foreach ($installconditions as $type => $conditions) {
  79274. if (!isset($conditions[0])) {
  79275. $conditions = array($conditions);
  79276. }
  79277. foreach ($conditions as $condition) {
  79278. $ret = $depchecker->{"validate{$type}Dependency"}($condition);
  79279. if (PEAR::isError($ret)) {
  79280. PEAR::popErrorHandling();
  79281. continue 3; // skip this release
  79282. }
  79283. }
  79284. }
  79285. PEAR::popErrorHandling();
  79286. }
  79287. }
  79288. // this is the release to use
  79289. if (isset($instance['filelist'])) {
  79290. // ignore files
  79291. if (isset($instance['filelist']['ignore'])) {
  79292. $ignore = isset($instance['filelist']['ignore'][0]) ?
  79293. $instance['filelist']['ignore'] :
  79294. array($instance['filelist']['ignore']);
  79295. foreach ($ignore as $ig) {
  79296. unset ($contents[$ig['attribs']['name']]);
  79297. }
  79298. }
  79299. // install files as this name
  79300. if (isset($instance['filelist']['install'])) {
  79301. $installas = isset($instance['filelist']['install'][0]) ?
  79302. $instance['filelist']['install'] :
  79303. array($instance['filelist']['install']);
  79304. foreach ($installas as $as) {
  79305. $contents[$as['attribs']['name']]['attribs']['install-as'] =
  79306. $as['attribs']['as'];
  79307. }
  79308. }
  79309. }
  79310. if ($forfilecheck) {
  79311. foreach ($contents as $file => $attrs) {
  79312. $contents[$file] = $attrs['attribs'];
  79313. }
  79314. }
  79315. return $contents;
  79316. }
  79317. } else { // simple release - no installconditions or install-as
  79318. if ($forfilecheck) {
  79319. return $this->getFilelist();
  79320. }
  79321. return $contents;
  79322. }
  79323. // no releases matched
  79324. return PEAR::raiseError('No releases in package.xml matched the existing operating ' .
  79325. 'system, extensions installed, or architecture, cannot install');
  79326. }
  79327. /**
  79328. * This is only used at install-time, after all serialization
  79329. * is over.
  79330. * @param string file name
  79331. * @param string installed path
  79332. */
  79333. function setInstalledAs($file, $path)
  79334. {
  79335. if ($path) {
  79336. return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
  79337. }
  79338. unset($this->_packageInfo['filelist'][$file]['installed_as']);
  79339. }
  79340. function getInstalledLocation($file)
  79341. {
  79342. if (isset($this->_packageInfo['filelist'][$file]['installed_as'])) {
  79343. return $this->_packageInfo['filelist'][$file]['installed_as'];
  79344. }
  79345. return false;
  79346. }
  79347. /**
  79348. * This is only used at install-time, after all serialization
  79349. * is over.
  79350. */
  79351. function installedFile($file, $atts)
  79352. {
  79353. if (isset($this->_packageInfo['filelist'][$file])) {
  79354. $this->_packageInfo['filelist'][$file] =
  79355. array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']);
  79356. } else {
  79357. $this->_packageInfo['filelist'][$file] = $atts['attribs'];
  79358. }
  79359. }
  79360. /**
  79361. * Retrieve the contents tag
  79362. */
  79363. function getContents()
  79364. {
  79365. if (isset($this->_packageInfo['contents'])) {
  79366. return $this->_packageInfo['contents'];
  79367. }
  79368. return false;
  79369. }
  79370. /**
  79371. * @param string full path to file
  79372. * @param string attribute name
  79373. * @param string attribute value
  79374. * @param int risky but fast - use this to choose a file based on its position in the list
  79375. * of files. Index is zero-based like PHP arrays.
  79376. * @return bool success of operation
  79377. */
  79378. function setFileAttribute($filename, $attr, $value, $index = false)
  79379. {
  79380. $this->_isValid = 0;
  79381. if (in_array($attr, array('role', 'name', 'baseinstalldir'))) {
  79382. $this->_filesValid = false;
  79383. }
  79384. if ($index !== false &&
  79385. isset($this->_packageInfo['contents']['dir']['file'][$index]['attribs'])) {
  79386. $this->_packageInfo['contents']['dir']['file'][$index]['attribs'][$attr] = $value;
  79387. return true;
  79388. }
  79389. if (!isset($this->_packageInfo['contents']['dir']['file'])) {
  79390. return false;
  79391. }
  79392. $files = $this->_packageInfo['contents']['dir']['file'];
  79393. if (!isset($files[0])) {
  79394. $files = array($files);
  79395. $ind = false;
  79396. } else {
  79397. $ind = true;
  79398. }
  79399. foreach ($files as $i => $file) {
  79400. if (isset($file['attribs'])) {
  79401. if ($file['attribs']['name'] == $filename) {
  79402. if ($ind) {
  79403. $this->_packageInfo['contents']['dir']['file'][$i]['attribs'][$attr] = $value;
  79404. } else {
  79405. $this->_packageInfo['contents']['dir']['file']['attribs'][$attr] = $value;
  79406. }
  79407. return true;
  79408. }
  79409. }
  79410. }
  79411. return false;
  79412. }
  79413. function setDirtree($path)
  79414. {
  79415. if (!isset($this->_packageInfo['dirtree'])) {
  79416. $this->_packageInfo['dirtree'] = array();
  79417. }
  79418. $this->_packageInfo['dirtree'][$path] = true;
  79419. }
  79420. function getDirtree()
  79421. {
  79422. if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
  79423. return $this->_packageInfo['dirtree'];
  79424. }
  79425. return false;
  79426. }
  79427. function resetDirtree()
  79428. {
  79429. unset($this->_packageInfo['dirtree']);
  79430. }
  79431. /**
  79432. * Determines whether this package claims it is compatible with the version of
  79433. * the package that has a recommended version dependency
  79434. * @param PEAR_PackageFile_v2|PEAR_PackageFile_v1|PEAR_Downloader_Package
  79435. * @return boolean
  79436. */
  79437. function isCompatible($pf)
  79438. {
  79439. if (!isset($this->_packageInfo['compatible'])) {
  79440. return false;
  79441. }
  79442. if (!isset($this->_packageInfo['channel'])) {
  79443. return false;
  79444. }
  79445. $me = $pf->getVersion();
  79446. $compatible = $this->_packageInfo['compatible'];
  79447. if (!isset($compatible[0])) {
  79448. $compatible = array($compatible);
  79449. }
  79450. $found = false;
  79451. foreach ($compatible as $info) {
  79452. if (strtolower($info['name']) == strtolower($pf->getPackage())) {
  79453. if (strtolower($info['channel']) == strtolower($pf->getChannel())) {
  79454. $found = true;
  79455. break;
  79456. }
  79457. }
  79458. }
  79459. if (!$found) {
  79460. return false;
  79461. }
  79462. if (isset($info['exclude'])) {
  79463. if (!isset($info['exclude'][0])) {
  79464. $info['exclude'] = array($info['exclude']);
  79465. }
  79466. foreach ($info['exclude'] as $exclude) {
  79467. if (version_compare($me, $exclude, '==')) {
  79468. return false;
  79469. }
  79470. }
  79471. }
  79472. if (version_compare($me, $info['min'], '>=') && version_compare($me, $info['max'], '<=')) {
  79473. return true;
  79474. }
  79475. return false;
  79476. }
  79477. /**
  79478. * @return array|false
  79479. */
  79480. function getCompatible()
  79481. {
  79482. if (isset($this->_packageInfo['compatible'])) {
  79483. return $this->_packageInfo['compatible'];
  79484. }
  79485. return false;
  79486. }
  79487. function getDependencies()
  79488. {
  79489. if (isset($this->_packageInfo['dependencies'])) {
  79490. return $this->_packageInfo['dependencies'];
  79491. }
  79492. return false;
  79493. }
  79494. function isSubpackageOf($p)
  79495. {
  79496. return $p->isSubpackage($this);
  79497. }
  79498. /**
  79499. * Determines whether the passed in package is a subpackage of this package.
  79500. *
  79501. * No version checking is done, only name verification.
  79502. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  79503. * @return bool
  79504. */
  79505. function isSubpackage($p)
  79506. {
  79507. $sub = array();
  79508. if (isset($this->_packageInfo['dependencies']['required']['subpackage'])) {
  79509. $sub = $this->_packageInfo['dependencies']['required']['subpackage'];
  79510. if (!isset($sub[0])) {
  79511. $sub = array($sub);
  79512. }
  79513. }
  79514. if (isset($this->_packageInfo['dependencies']['optional']['subpackage'])) {
  79515. $sub1 = $this->_packageInfo['dependencies']['optional']['subpackage'];
  79516. if (!isset($sub1[0])) {
  79517. $sub1 = array($sub1);
  79518. }
  79519. $sub = array_merge($sub, $sub1);
  79520. }
  79521. if (isset($this->_packageInfo['dependencies']['group'])) {
  79522. $group = $this->_packageInfo['dependencies']['group'];
  79523. if (!isset($group[0])) {
  79524. $group = array($group);
  79525. }
  79526. foreach ($group as $deps) {
  79527. if (isset($deps['subpackage'])) {
  79528. $sub2 = $deps['subpackage'];
  79529. if (!isset($sub2[0])) {
  79530. $sub2 = array($sub2);
  79531. }
  79532. $sub = array_merge($sub, $sub2);
  79533. }
  79534. }
  79535. }
  79536. foreach ($sub as $dep) {
  79537. if (strtolower($dep['name']) == strtolower($p->getPackage())) {
  79538. if (isset($dep['channel'])) {
  79539. if (strtolower($dep['channel']) == strtolower($p->getChannel())) {
  79540. return true;
  79541. }
  79542. } else {
  79543. if ($dep['uri'] == $p->getURI()) {
  79544. return true;
  79545. }
  79546. }
  79547. }
  79548. }
  79549. return false;
  79550. }
  79551. function dependsOn($package, $channel)
  79552. {
  79553. if (!($deps = $this->getDependencies())) {
  79554. return false;
  79555. }
  79556. foreach (array('package', 'subpackage') as $type) {
  79557. foreach (array('required', 'optional') as $needed) {
  79558. if (isset($deps[$needed][$type])) {
  79559. if (!isset($deps[$needed][$type][0])) {
  79560. $deps[$needed][$type] = array($deps[$needed][$type]);
  79561. }
  79562. foreach ($deps[$needed][$type] as $dep) {
  79563. $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
  79564. if (strtolower($dep['name']) == strtolower($package) &&
  79565. $depchannel == $channel) {
  79566. return true;
  79567. }
  79568. }
  79569. }
  79570. }
  79571. if (isset($deps['group'])) {
  79572. if (!isset($deps['group'][0])) {
  79573. $dep['group'] = array($deps['group']);
  79574. }
  79575. foreach ($deps['group'] as $group) {
  79576. if (isset($group[$type])) {
  79577. if (!is_array($group[$type])) {
  79578. $group[$type] = array($group[$type]);
  79579. }
  79580. foreach ($group[$type] as $dep) {
  79581. $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
  79582. if (strtolower($dep['name']) == strtolower($package) &&
  79583. $depchannel == $channel) {
  79584. return true;
  79585. }
  79586. }
  79587. }
  79588. }
  79589. }
  79590. }
  79591. return false;
  79592. }
  79593. /**
  79594. * Get the contents of a dependency group
  79595. * @param string
  79596. * @return array|false
  79597. */
  79598. function getDependencyGroup($name)
  79599. {
  79600. $name = strtolower($name);
  79601. if (!isset($this->_packageInfo['dependencies']['group'])) {
  79602. return false;
  79603. }
  79604. $groups = $this->_packageInfo['dependencies']['group'];
  79605. if (!isset($groups[0])) {
  79606. $groups = array($groups);
  79607. }
  79608. foreach ($groups as $group) {
  79609. if (strtolower($group['attribs']['name']) == $name) {
  79610. return $group;
  79611. }
  79612. }
  79613. return false;
  79614. }
  79615. /**
  79616. * Retrieve a partial package.xml 1.0 representation of dependencies
  79617. *
  79618. * a very limited representation of dependencies is returned by this method.
  79619. * The <exclude> tag for excluding certain versions of a dependency is
  79620. * completely ignored. In addition, dependency groups are ignored, with the
  79621. * assumption that all dependencies in dependency groups are also listed in
  79622. * the optional group that work with all dependency groups
  79623. * @param boolean return package.xml 2.0 <dependencies> tag
  79624. * @return array|false
  79625. */
  79626. function getDeps($raw = false, $nopearinstaller = false)
  79627. {
  79628. if (isset($this->_packageInfo['dependencies'])) {
  79629. if ($raw) {
  79630. return $this->_packageInfo['dependencies'];
  79631. }
  79632. $ret = array();
  79633. $map = array(
  79634. 'php' => 'php',
  79635. 'package' => 'pkg',
  79636. 'subpackage' => 'pkg',
  79637. 'extension' => 'ext',
  79638. 'os' => 'os',
  79639. 'pearinstaller' => 'pkg',
  79640. );
  79641. foreach (array('required', 'optional') as $type) {
  79642. $optional = ($type == 'optional') ? 'yes' : 'no';
  79643. if (!isset($this->_packageInfo['dependencies'][$type])
  79644. || empty($this->_packageInfo['dependencies'][$type])) {
  79645. continue;
  79646. }
  79647. foreach ($this->_packageInfo['dependencies'][$type] as $dtype => $deps) {
  79648. if ($dtype == 'pearinstaller' && $nopearinstaller) {
  79649. continue;
  79650. }
  79651. if ((is_array($deps) && !isset($deps[0])) || !is_array($deps)) {
  79652. $deps = array($deps);
  79653. }
  79654. foreach ($deps as $dep) {
  79655. if (!isset($map[$dtype])) {
  79656. // no support for arch type
  79657. continue;
  79658. }
  79659. if ($dtype == 'pearinstaller') {
  79660. $dep['name'] = 'PEAR';
  79661. $dep['channel'] = 'pear.php.net';
  79662. }
  79663. $s = array('type' => $map[$dtype]);
  79664. if (isset($dep['channel'])) {
  79665. $s['channel'] = $dep['channel'];
  79666. }
  79667. if (isset($dep['uri'])) {
  79668. $s['uri'] = $dep['uri'];
  79669. }
  79670. if (isset($dep['name'])) {
  79671. $s['name'] = $dep['name'];
  79672. }
  79673. if (isset($dep['conflicts'])) {
  79674. $s['rel'] = 'not';
  79675. } else {
  79676. if (!isset($dep['min']) &&
  79677. !isset($dep['max'])) {
  79678. $s['rel'] = 'has';
  79679. $s['optional'] = $optional;
  79680. } elseif (isset($dep['min']) &&
  79681. isset($dep['max'])) {
  79682. $s['rel'] = 'ge';
  79683. $s1 = $s;
  79684. $s1['rel'] = 'le';
  79685. $s['version'] = $dep['min'];
  79686. $s1['version'] = $dep['max'];
  79687. if (isset($dep['channel'])) {
  79688. $s1['channel'] = $dep['channel'];
  79689. }
  79690. if ($dtype != 'php') {
  79691. $s['name'] = $dep['name'];
  79692. $s1['name'] = $dep['name'];
  79693. }
  79694. $s['optional'] = $optional;
  79695. $s1['optional'] = $optional;
  79696. $ret[] = $s1;
  79697. } elseif (isset($dep['min'])) {
  79698. if (isset($dep['exclude']) &&
  79699. $dep['exclude'] == $dep['min']) {
  79700. $s['rel'] = 'gt';
  79701. } else {
  79702. $s['rel'] = 'ge';
  79703. }
  79704. $s['version'] = $dep['min'];
  79705. $s['optional'] = $optional;
  79706. if ($dtype != 'php') {
  79707. $s['name'] = $dep['name'];
  79708. }
  79709. } elseif (isset($dep['max'])) {
  79710. if (isset($dep['exclude']) &&
  79711. $dep['exclude'] == $dep['max']) {
  79712. $s['rel'] = 'lt';
  79713. } else {
  79714. $s['rel'] = 'le';
  79715. }
  79716. $s['version'] = $dep['max'];
  79717. $s['optional'] = $optional;
  79718. if ($dtype != 'php') {
  79719. $s['name'] = $dep['name'];
  79720. }
  79721. }
  79722. }
  79723. $ret[] = $s;
  79724. }
  79725. }
  79726. }
  79727. if (count($ret)) {
  79728. return $ret;
  79729. }
  79730. }
  79731. return false;
  79732. }
  79733. /**
  79734. * @return php|extsrc|extbin|zendextsrc|zendextbin|bundle|false
  79735. */
  79736. function getPackageType()
  79737. {
  79738. if (isset($this->_packageInfo['phprelease'])) {
  79739. return 'php';
  79740. }
  79741. if (isset($this->_packageInfo['extsrcrelease'])) {
  79742. return 'extsrc';
  79743. }
  79744. if (isset($this->_packageInfo['extbinrelease'])) {
  79745. return 'extbin';
  79746. }
  79747. if (isset($this->_packageInfo['zendextsrcrelease'])) {
  79748. return 'zendextsrc';
  79749. }
  79750. if (isset($this->_packageInfo['zendextbinrelease'])) {
  79751. return 'zendextbin';
  79752. }
  79753. if (isset($this->_packageInfo['bundle'])) {
  79754. return 'bundle';
  79755. }
  79756. return false;
  79757. }
  79758. /**
  79759. * @return array|false
  79760. */
  79761. function getReleases()
  79762. {
  79763. $type = $this->getPackageType();
  79764. if ($type != 'bundle') {
  79765. $type .= 'release';
  79766. }
  79767. if ($this->getPackageType() && isset($this->_packageInfo[$type])) {
  79768. return $this->_packageInfo[$type];
  79769. }
  79770. return false;
  79771. }
  79772. /**
  79773. * @return array
  79774. */
  79775. function getChangelog()
  79776. {
  79777. if (isset($this->_packageInfo['changelog'])) {
  79778. return $this->_packageInfo['changelog'];
  79779. }
  79780. return false;
  79781. }
  79782. function hasDeps()
  79783. {
  79784. return isset($this->_packageInfo['dependencies']);
  79785. }
  79786. function getPackagexmlVersion()
  79787. {
  79788. if (isset($this->_packageInfo['zendextsrcrelease'])) {
  79789. return '2.1';
  79790. }
  79791. if (isset($this->_packageInfo['zendextbinrelease'])) {
  79792. return '2.1';
  79793. }
  79794. return '2.0';
  79795. }
  79796. /**
  79797. * @return array|false
  79798. */
  79799. function getSourcePackage()
  79800. {
  79801. if (isset($this->_packageInfo['extbinrelease']) ||
  79802. isset($this->_packageInfo['zendextbinrelease'])) {
  79803. return array('channel' => $this->_packageInfo['srcchannel'],
  79804. 'package' => $this->_packageInfo['srcpackage']);
  79805. }
  79806. return false;
  79807. }
  79808. function getBundledPackages()
  79809. {
  79810. if (isset($this->_packageInfo['bundle'])) {
  79811. return $this->_packageInfo['contents']['bundledpackage'];
  79812. }
  79813. return false;
  79814. }
  79815. function getLastModified()
  79816. {
  79817. if (isset($this->_packageInfo['_lastmodified'])) {
  79818. return $this->_packageInfo['_lastmodified'];
  79819. }
  79820. return false;
  79821. }
  79822. /**
  79823. * Get the contents of a file listed within the package.xml
  79824. * @param string
  79825. * @return string
  79826. */
  79827. function getFileContents($file)
  79828. {
  79829. if ($this->_archiveFile == $this->_packageFile) { // unpacked
  79830. $dir = dirname($this->_packageFile);
  79831. $file = $dir . DIRECTORY_SEPARATOR . $file;
  79832. $file = str_replace(array('/', '\\'),
  79833. array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
  79834. if (file_exists($file) && is_readable($file)) {
  79835. return implode('', file($file));
  79836. }
  79837. } else { // tgz
  79838. $tar = new Archive_Tar($this->_archiveFile);
  79839. $tar->pushErrorHandling(PEAR_ERROR_RETURN);
  79840. if ($file != 'package.xml' && $file != 'package2.xml') {
  79841. $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
  79842. }
  79843. $file = $tar->extractInString($file);
  79844. $tar->popErrorHandling();
  79845. if (PEAR::isError($file)) {
  79846. return PEAR::raiseError("Cannot locate file '$file' in archive");
  79847. }
  79848. return $file;
  79849. }
  79850. }
  79851. function &getRW()
  79852. {
  79853. if (!class_exists('PEAR_PackageFile_v2_rw')) {
  79854. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/v2/rw.php';
  79855. }
  79856. $a = new PEAR_PackageFile_v2_rw;
  79857. foreach (get_object_vars($this) as $name => $unused) {
  79858. if (!isset($this->$name)) {
  79859. continue;
  79860. }
  79861. if ($name == '_config' || $name == '_logger'|| $name == '_registry' ||
  79862. $name == '_stack') {
  79863. $a->$name = &$this->$name;
  79864. } else {
  79865. $a->$name = $this->$name;
  79866. }
  79867. }
  79868. return $a;
  79869. }
  79870. function &getDefaultGenerator()
  79871. {
  79872. if (!class_exists('PEAR_PackageFile_Generator_v2')) {
  79873. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/Generator/v2.php';
  79874. }
  79875. $a = new PEAR_PackageFile_Generator_v2($this);
  79876. return $a;
  79877. }
  79878. function analyzeSourceCode($file, $string = false)
  79879. {
  79880. if (!isset($this->_v2Validator) ||
  79881. !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
  79882. if (!class_exists('PEAR_PackageFile_v2_Validator')) {
  79883. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/v2/Validator.php';
  79884. }
  79885. $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
  79886. }
  79887. return $this->_v2Validator->analyzeSourceCode($file, $string);
  79888. }
  79889. function validate($state = PEAR_VALIDATE_NORMAL)
  79890. {
  79891. if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
  79892. return false;
  79893. }
  79894. if (!isset($this->_v2Validator) ||
  79895. !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
  79896. if (!class_exists('PEAR_PackageFile_v2_Validator')) {
  79897. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/v2/Validator.php';
  79898. }
  79899. $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
  79900. }
  79901. if (isset($this->_packageInfo['xsdversion'])) {
  79902. unset($this->_packageInfo['xsdversion']);
  79903. }
  79904. return $this->_v2Validator->validate($this, $state);
  79905. }
  79906. function getTasksNs()
  79907. {
  79908. if (!isset($this->_tasksNs)) {
  79909. if (isset($this->_packageInfo['attribs'])) {
  79910. foreach ($this->_packageInfo['attribs'] as $name => $value) {
  79911. if ($value == 'http://pear.php.net/dtd/tasks-1.0') {
  79912. $this->_tasksNs = str_replace('xmlns:', '', $name);
  79913. break;
  79914. }
  79915. }
  79916. }
  79917. }
  79918. return $this->_tasksNs;
  79919. }
  79920. /**
  79921. * Determine whether a task name is a valid task. Custom tasks may be defined
  79922. * using subdirectories by putting a "-" in the name, as in <tasks:mycustom-task>
  79923. *
  79924. * Note that this method will auto-load the task class file and test for the existence
  79925. * of the name with "-" replaced by "_" as in PEAR/Task/mycustom/task.php makes class
  79926. * PEAR_Task_mycustom_task
  79927. * @param string
  79928. * @return boolean
  79929. */
  79930. function getTask($task)
  79931. {
  79932. $this->getTasksNs();
  79933. // transform all '-' to '/' and 'tasks:' to '' so tasks:replace becomes replace
  79934. $task = str_replace(array($this->_tasksNs . ':', '-'), array('', ' '), $task);
  79935. $taskfile = str_replace(' ', '/', ucwords($task));
  79936. $task = str_replace(array(' ', '/'), '_', ucwords($task));
  79937. if (class_exists("PEAR_Task_$task")) {
  79938. return "PEAR_Task_$task";
  79939. }
  79940. $fp = @fopen("phar://go-pear.phar/PEAR/Task/$taskfile.php", 'r', true);
  79941. if ($fp) {
  79942. fclose($fp);
  79943. require_once 'phar://go-pear.phar/' . "PEAR/Task/$taskfile.php";
  79944. return "PEAR_Task_$task";
  79945. }
  79946. return false;
  79947. }
  79948. /**
  79949. * Key-friendly array_splice
  79950. * @param tagname to splice a value in before
  79951. * @param mixed the value to splice in
  79952. * @param string the new tag name
  79953. */
  79954. function _ksplice($array, $key, $value, $newkey)
  79955. {
  79956. $offset = array_search($key, array_keys($array));
  79957. $after = array_slice($array, $offset);
  79958. $before = array_slice($array, 0, $offset);
  79959. $before[$newkey] = $value;
  79960. return array_merge($before, $after);
  79961. }
  79962. /**
  79963. * @param array a list of possible keys, in the order they may occur
  79964. * @param mixed contents of the new package.xml tag
  79965. * @param string tag name
  79966. * @access private
  79967. */
  79968. function _insertBefore($array, $keys, $contents, $newkey)
  79969. {
  79970. foreach ($keys as $key) {
  79971. if (isset($array[$key])) {
  79972. return $array = $this->_ksplice($array, $key, $contents, $newkey);
  79973. }
  79974. }
  79975. $array[$newkey] = $contents;
  79976. return $array;
  79977. }
  79978. /**
  79979. * @param subsection of {@link $_packageInfo}
  79980. * @param array|string tag contents
  79981. * @param array format:
  79982. * <pre>
  79983. * array(
  79984. * tagname => array(list of tag names that follow this one),
  79985. * childtagname => array(list of child tag names that follow this one),
  79986. * )
  79987. * </pre>
  79988. *
  79989. * This allows construction of nested tags
  79990. * @access private
  79991. */
  79992. function _mergeTag($manip, $contents, $order)
  79993. {
  79994. if (count($order)) {
  79995. foreach ($order as $tag => $curorder) {
  79996. if (!isset($manip[$tag])) {
  79997. // ensure that the tag is set up
  79998. $manip = $this->_insertBefore($manip, $curorder, array(), $tag);
  79999. }
  80000. if (count($order) > 1) {
  80001. $manip[$tag] = $this->_mergeTag($manip[$tag], $contents, array_slice($order, 1));
  80002. return $manip;
  80003. }
  80004. }
  80005. } else {
  80006. return $manip;
  80007. }
  80008. if (is_array($manip[$tag]) && !empty($manip[$tag]) && isset($manip[$tag][0])) {
  80009. $manip[$tag][] = $contents;
  80010. } else {
  80011. if (is_array($manip[$tag]) && !count($manip[$tag])) {
  80012. $manip[$tag] = $contents;
  80013. } else {
  80014. $manip[$tag] = array($manip[$tag]);
  80015. $manip[$tag][] = $contents;
  80016. }
  80017. }
  80018. return $manip;
  80019. }
  80020. }
  80021. ?>
  80022. <?php
  80023. /**
  80024. * PEAR_PackageFile_v2, package.xml version 2.0, read/write version
  80025. *
  80026. * PHP versions 4 and 5
  80027. *
  80028. * @category pear
  80029. * @package PEAR
  80030. * @author Greg Beaver <cellog@php.net>
  80031. * @copyright 1997-2009 The Authors
  80032. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  80033. * @link http://pear.php.net/package/PEAR
  80034. * @since File available since Release 1.4.0a8
  80035. */
  80036. /**
  80037. * Private validation class used by PEAR_PackageFile_v2 - do not use directly, its
  80038. * sole purpose is to split up the PEAR/PackageFile/v2.php file to make it smaller
  80039. * @category pear
  80040. * @package PEAR
  80041. * @author Greg Beaver <cellog@php.net>
  80042. * @copyright 1997-2009 The Authors
  80043. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  80044. * @version Release: 1.10.16
  80045. * @link http://pear.php.net/package/PEAR
  80046. * @since Class available since Release 1.4.0a8
  80047. * @access private
  80048. */
  80049. class PEAR_PackageFile_v2_Validator
  80050. {
  80051. /**
  80052. * @var array
  80053. */
  80054. var $_packageInfo;
  80055. /**
  80056. * @var PEAR_PackageFile_v2
  80057. */
  80058. var $_pf;
  80059. /**
  80060. * @var PEAR_ErrorStack
  80061. */
  80062. var $_stack;
  80063. /**
  80064. * @var int
  80065. */
  80066. var $_isValid = 0;
  80067. /**
  80068. * @var int
  80069. */
  80070. var $_filesValid = 0;
  80071. /**
  80072. * @var int
  80073. */
  80074. var $_curState = 0;
  80075. /**
  80076. * @param PEAR_PackageFile_v2
  80077. * @param int
  80078. */
  80079. function validate(&$pf, $state = PEAR_VALIDATE_NORMAL)
  80080. {
  80081. $this->_pf = &$pf;
  80082. $this->_curState = $state;
  80083. $this->_packageInfo = $this->_pf->getArray();
  80084. $this->_isValid = $this->_pf->_isValid;
  80085. $this->_filesValid = $this->_pf->_filesValid;
  80086. $this->_stack = &$pf->_stack;
  80087. $this->_stack->getErrors(true);
  80088. if (($this->_isValid & $state) == $state) {
  80089. return true;
  80090. }
  80091. if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
  80092. return false;
  80093. }
  80094. if (!isset($this->_packageInfo['attribs']['version']) ||
  80095. ($this->_packageInfo['attribs']['version'] != '2.0' &&
  80096. $this->_packageInfo['attribs']['version'] != '2.1')
  80097. ) {
  80098. $this->_noPackageVersion();
  80099. }
  80100. $structure =
  80101. array(
  80102. 'name',
  80103. 'channel|uri',
  80104. '*extends', // can't be multiple, but this works fine
  80105. 'summary',
  80106. 'description',
  80107. '+lead', // these all need content checks
  80108. '*developer',
  80109. '*contributor',
  80110. '*helper',
  80111. 'date',
  80112. '*time',
  80113. 'version',
  80114. 'stability',
  80115. 'license->?uri->?filesource',
  80116. 'notes',
  80117. 'contents', //special validation needed
  80118. '*compatible',
  80119. 'dependencies', //special validation needed
  80120. '*usesrole',
  80121. '*usestask', // reserve these for 1.4.0a1 to implement
  80122. // this will allow a package.xml to gracefully say it
  80123. // needs a certain package installed in order to implement a role or task
  80124. '*providesextension',
  80125. '*srcpackage|*srcuri',
  80126. '+phprelease|+extsrcrelease|+extbinrelease|' .
  80127. '+zendextsrcrelease|+zendextbinrelease|bundle', //special validation needed
  80128. '*changelog',
  80129. );
  80130. $test = $this->_packageInfo;
  80131. if (isset($test['dependencies']) &&
  80132. isset($test['dependencies']['required']) &&
  80133. isset($test['dependencies']['required']['pearinstaller']) &&
  80134. isset($test['dependencies']['required']['pearinstaller']['min']) &&
  80135. '1.10.16' != '@package' . '_version@' &&
  80136. version_compare('1.10.16',
  80137. $test['dependencies']['required']['pearinstaller']['min'], '<')
  80138. ) {
  80139. $this->_pearVersionTooLow($test['dependencies']['required']['pearinstaller']['min']);
  80140. return false;
  80141. }
  80142. // ignore post-installation array fields
  80143. if (array_key_exists('filelist', $test)) {
  80144. unset($test['filelist']);
  80145. }
  80146. if (array_key_exists('_lastmodified', $test)) {
  80147. unset($test['_lastmodified']);
  80148. }
  80149. if (array_key_exists('#binarypackage', $test)) {
  80150. unset($test['#binarypackage']);
  80151. }
  80152. if (array_key_exists('old', $test)) {
  80153. unset($test['old']);
  80154. }
  80155. if (array_key_exists('_lastversion', $test)) {
  80156. unset($test['_lastversion']);
  80157. }
  80158. if (!$this->_stupidSchemaValidate($structure, $test, '<package>')) {
  80159. return false;
  80160. }
  80161. if (empty($this->_packageInfo['name'])) {
  80162. $this->_tagCannotBeEmpty('name');
  80163. }
  80164. $test = isset($this->_packageInfo['uri']) ? 'uri' :'channel';
  80165. if (empty($this->_packageInfo[$test])) {
  80166. $this->_tagCannotBeEmpty($test);
  80167. }
  80168. if (is_array($this->_packageInfo['license']) &&
  80169. (!isset($this->_packageInfo['license']['_content']) ||
  80170. empty($this->_packageInfo['license']['_content']))) {
  80171. $this->_tagCannotBeEmpty('license');
  80172. } elseif (empty($this->_packageInfo['license'])) {
  80173. $this->_tagCannotBeEmpty('license');
  80174. }
  80175. if (empty($this->_packageInfo['summary'])) {
  80176. $this->_tagCannotBeEmpty('summary');
  80177. }
  80178. if (empty($this->_packageInfo['description'])) {
  80179. $this->_tagCannotBeEmpty('description');
  80180. }
  80181. if (empty($this->_packageInfo['date'])) {
  80182. $this->_tagCannotBeEmpty('date');
  80183. }
  80184. if (empty($this->_packageInfo['notes'])) {
  80185. $this->_tagCannotBeEmpty('notes');
  80186. }
  80187. if (isset($this->_packageInfo['time']) && empty($this->_packageInfo['time'])) {
  80188. $this->_tagCannotBeEmpty('time');
  80189. }
  80190. if (isset($this->_packageInfo['dependencies'])) {
  80191. $this->_validateDependencies();
  80192. }
  80193. if (isset($this->_packageInfo['compatible'])) {
  80194. $this->_validateCompatible();
  80195. }
  80196. if (!isset($this->_packageInfo['bundle'])) {
  80197. if (empty($this->_packageInfo['contents'])) {
  80198. $this->_tagCannotBeEmpty('contents');
  80199. }
  80200. if (!isset($this->_packageInfo['contents']['dir'])) {
  80201. $this->_filelistMustContainDir('contents');
  80202. return false;
  80203. }
  80204. if (isset($this->_packageInfo['contents']['file'])) {
  80205. $this->_filelistCannotContainFile('contents');
  80206. return false;
  80207. }
  80208. }
  80209. $this->_validateMaintainers();
  80210. $this->_validateStabilityVersion();
  80211. $fail = false;
  80212. if (array_key_exists('usesrole', $this->_packageInfo)) {
  80213. $roles = $this->_packageInfo['usesrole'];
  80214. if (!is_array($roles) || !isset($roles[0])) {
  80215. $roles = array($roles);
  80216. }
  80217. foreach ($roles as $role) {
  80218. if (!isset($role['role'])) {
  80219. $this->_usesroletaskMustHaveRoleTask('usesrole', 'role');
  80220. $fail = true;
  80221. } else {
  80222. if (!isset($role['channel'])) {
  80223. if (!isset($role['uri'])) {
  80224. $this->_usesroletaskMustHaveChannelOrUri($role['role'], 'usesrole');
  80225. $fail = true;
  80226. }
  80227. } elseif (!isset($role['package'])) {
  80228. $this->_usesroletaskMustHavePackage($role['role'], 'usesrole');
  80229. $fail = true;
  80230. }
  80231. }
  80232. }
  80233. }
  80234. if (array_key_exists('usestask', $this->_packageInfo)) {
  80235. $roles = $this->_packageInfo['usestask'];
  80236. if (!is_array($roles) || !isset($roles[0])) {
  80237. $roles = array($roles);
  80238. }
  80239. foreach ($roles as $role) {
  80240. if (!isset($role['task'])) {
  80241. $this->_usesroletaskMustHaveRoleTask('usestask', 'task');
  80242. $fail = true;
  80243. } else {
  80244. if (!isset($role['channel'])) {
  80245. if (!isset($role['uri'])) {
  80246. $this->_usesroletaskMustHaveChannelOrUri($role['task'], 'usestask');
  80247. $fail = true;
  80248. }
  80249. } elseif (!isset($role['package'])) {
  80250. $this->_usesroletaskMustHavePackage($role['task'], 'usestask');
  80251. $fail = true;
  80252. }
  80253. }
  80254. }
  80255. }
  80256. if ($fail) {
  80257. return false;
  80258. }
  80259. $list = $this->_packageInfo['contents'];
  80260. if (isset($list['dir']) && is_array($list['dir']) && isset($list['dir'][0])) {
  80261. $this->_multipleToplevelDirNotAllowed();
  80262. return $this->_isValid = 0;
  80263. }
  80264. $this->_validateFilelist();
  80265. $this->_validateRelease();
  80266. if (!$this->_stack->hasErrors()) {
  80267. $chan = $this->_pf->_registry->getChannel($this->_pf->getChannel(), true);
  80268. if (PEAR::isError($chan)) {
  80269. $this->_unknownChannel($this->_pf->getChannel());
  80270. } else {
  80271. $valpack = $chan->getValidationPackage();
  80272. // for channel validator packages, always use the default PEAR validator.
  80273. // otherwise, they can't be installed or packaged
  80274. $validator = $chan->getValidationObject($this->_pf->getPackage());
  80275. if (!$validator) {
  80276. $this->_stack->push(__FUNCTION__, 'error',
  80277. array('channel' => $chan->getName(),
  80278. 'package' => $this->_pf->getPackage(),
  80279. 'name' => $valpack['_content'],
  80280. 'version' => $valpack['attribs']['version']),
  80281. 'package "%channel%/%package%" cannot be properly validated without ' .
  80282. 'validation package "%channel%/%name%-%version%"');
  80283. return $this->_isValid = 0;
  80284. }
  80285. $validator->setPackageFile($this->_pf);
  80286. $validator->validate($state);
  80287. $failures = $validator->getFailures();
  80288. foreach ($failures['errors'] as $error) {
  80289. $this->_stack->push(__FUNCTION__, 'error', $error,
  80290. 'Channel validator error: field "%field%" - %reason%');
  80291. }
  80292. foreach ($failures['warnings'] as $warning) {
  80293. $this->_stack->push(__FUNCTION__, 'warning', $warning,
  80294. 'Channel validator warning: field "%field%" - %reason%');
  80295. }
  80296. }
  80297. }
  80298. $this->_pf->_isValid = $this->_isValid = !$this->_stack->hasErrors('error');
  80299. if ($this->_isValid && $state == PEAR_VALIDATE_PACKAGING && !$this->_filesValid) {
  80300. if ($this->_pf->getPackageType() == 'bundle') {
  80301. if ($this->_analyzeBundledPackages()) {
  80302. $this->_filesValid = $this->_pf->_filesValid = true;
  80303. } else {
  80304. $this->_pf->_isValid = $this->_isValid = 0;
  80305. }
  80306. } else {
  80307. if (!$this->_analyzePhpFiles()) {
  80308. $this->_pf->_isValid = $this->_isValid = 0;
  80309. } else {
  80310. $this->_filesValid = $this->_pf->_filesValid = true;
  80311. }
  80312. }
  80313. }
  80314. if ($this->_isValid) {
  80315. return $this->_pf->_isValid = $this->_isValid = $state;
  80316. }
  80317. return $this->_pf->_isValid = $this->_isValid = 0;
  80318. }
  80319. function _stupidSchemaValidate($structure, $xml, $root)
  80320. {
  80321. if (!is_array($xml)) {
  80322. $xml = array();
  80323. }
  80324. $keys = array_keys($xml);
  80325. reset($keys);
  80326. $key = current($keys);
  80327. while ($key == 'attribs' || $key == '_contents') {
  80328. $key = next($keys);
  80329. }
  80330. $unfoundtags = $optionaltags = array();
  80331. $ret = true;
  80332. $mismatch = false;
  80333. foreach ($structure as $struc) {
  80334. if ($key) {
  80335. $tag = $xml[$key];
  80336. }
  80337. $test = $this->_processStructure($struc);
  80338. if (isset($test['choices'])) {
  80339. $loose = true;
  80340. foreach ($test['choices'] as $choice) {
  80341. if ($key == $choice['tag']) {
  80342. $key = next($keys);
  80343. while ($key == 'attribs' || $key == '_contents') {
  80344. $key = next($keys);
  80345. }
  80346. $unfoundtags = $optionaltags = array();
  80347. $mismatch = false;
  80348. if ($key && $key != $choice['tag'] && isset($choice['multiple'])) {
  80349. $unfoundtags[] = $choice['tag'];
  80350. $optionaltags[] = $choice['tag'];
  80351. if ($key) {
  80352. $mismatch = true;
  80353. }
  80354. }
  80355. $ret &= $this->_processAttribs($choice, $tag, $root);
  80356. continue 2;
  80357. } else {
  80358. $unfoundtags[] = $choice['tag'];
  80359. $mismatch = true;
  80360. }
  80361. if (!isset($choice['multiple']) || $choice['multiple'] != '*') {
  80362. $loose = false;
  80363. } else {
  80364. $optionaltags[] = $choice['tag'];
  80365. }
  80366. }
  80367. if (!$loose) {
  80368. $this->_invalidTagOrder($unfoundtags, $key, $root);
  80369. return false;
  80370. }
  80371. } else {
  80372. if ($key != $test['tag']) {
  80373. if (isset($test['multiple']) && $test['multiple'] != '*') {
  80374. $unfoundtags[] = $test['tag'];
  80375. $this->_invalidTagOrder($unfoundtags, $key, $root);
  80376. return false;
  80377. } else {
  80378. if ($key) {
  80379. $mismatch = true;
  80380. }
  80381. $unfoundtags[] = $test['tag'];
  80382. $optionaltags[] = $test['tag'];
  80383. }
  80384. if (!isset($test['multiple'])) {
  80385. $this->_invalidTagOrder($unfoundtags, $key, $root);
  80386. return false;
  80387. }
  80388. continue;
  80389. } else {
  80390. $unfoundtags = $optionaltags = array();
  80391. $mismatch = false;
  80392. }
  80393. $key = next($keys);
  80394. while ($key == 'attribs' || $key == '_contents') {
  80395. $key = next($keys);
  80396. }
  80397. if ($key && $key != $test['tag'] && isset($test['multiple'])) {
  80398. $unfoundtags[] = $test['tag'];
  80399. $optionaltags[] = $test['tag'];
  80400. $mismatch = true;
  80401. }
  80402. $ret &= $this->_processAttribs($test, $tag, $root);
  80403. continue;
  80404. }
  80405. }
  80406. if (!$mismatch && count($optionaltags)) {
  80407. // don't error out on any optional tags
  80408. $unfoundtags = array_diff($unfoundtags, $optionaltags);
  80409. }
  80410. if (count($unfoundtags)) {
  80411. $this->_invalidTagOrder($unfoundtags, $key, $root);
  80412. } elseif ($key) {
  80413. // unknown tags
  80414. $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
  80415. while ($key = next($keys)) {
  80416. $this->_invalidTagOrder('*no tags allowed here*', $key, $root);
  80417. }
  80418. }
  80419. return $ret;
  80420. }
  80421. function _processAttribs($choice, $tag, $context)
  80422. {
  80423. if (isset($choice['attribs'])) {
  80424. if (!is_array($tag)) {
  80425. $tag = array($tag);
  80426. }
  80427. $tags = $tag;
  80428. if (!isset($tags[0])) {
  80429. $tags = array($tags);
  80430. }
  80431. $ret = true;
  80432. foreach ($tags as $i => $tag) {
  80433. if (!is_array($tag) || !isset($tag['attribs'])) {
  80434. foreach ($choice['attribs'] as $attrib) {
  80435. if ($attrib[0] != '?') {
  80436. $ret &= $this->_tagHasNoAttribs($choice['tag'],
  80437. $context);
  80438. continue 2;
  80439. }
  80440. }
  80441. }
  80442. foreach ($choice['attribs'] as $attrib) {
  80443. if ($attrib[0] != '?') {
  80444. if (!isset($tag['attribs'][$attrib])) {
  80445. $ret &= $this->_tagMissingAttribute($choice['tag'],
  80446. $attrib, $context);
  80447. }
  80448. }
  80449. }
  80450. }
  80451. return $ret;
  80452. }
  80453. return true;
  80454. }
  80455. function _processStructure($key)
  80456. {
  80457. $ret = array();
  80458. if (count($pieces = explode('|', $key)) > 1) {
  80459. $ret['choices'] = array();
  80460. foreach ($pieces as $piece) {
  80461. $ret['choices'][] = $this->_processStructure($piece);
  80462. }
  80463. return $ret;
  80464. }
  80465. $multi = $key[0];
  80466. if ($multi == '+' || $multi == '*') {
  80467. $ret['multiple'] = $key[0];
  80468. $key = substr($key, 1);
  80469. }
  80470. if (count($attrs = explode('->', $key)) > 1) {
  80471. $ret['tag'] = array_shift($attrs);
  80472. $ret['attribs'] = $attrs;
  80473. } else {
  80474. $ret['tag'] = $key;
  80475. }
  80476. return $ret;
  80477. }
  80478. function _validateStabilityVersion()
  80479. {
  80480. $structure = array('release', 'api');
  80481. $a = $this->_stupidSchemaValidate($structure, $this->_packageInfo['version'], '<version>');
  80482. $a &= $this->_stupidSchemaValidate($structure, $this->_packageInfo['stability'], '<stability>');
  80483. if ($a) {
  80484. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  80485. $this->_packageInfo['version']['release'])) {
  80486. $this->_invalidVersion('release', $this->_packageInfo['version']['release']);
  80487. }
  80488. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  80489. $this->_packageInfo['version']['api'])) {
  80490. $this->_invalidVersion('api', $this->_packageInfo['version']['api']);
  80491. }
  80492. if (!in_array($this->_packageInfo['stability']['release'],
  80493. array('snapshot', 'devel', 'alpha', 'beta', 'stable'))) {
  80494. $this->_invalidState('release', $this->_packageInfo['stability']['release']);
  80495. }
  80496. if (!in_array($this->_packageInfo['stability']['api'],
  80497. array('devel', 'alpha', 'beta', 'stable'))) {
  80498. $this->_invalidState('api', $this->_packageInfo['stability']['api']);
  80499. }
  80500. }
  80501. }
  80502. function _validateMaintainers()
  80503. {
  80504. $structure =
  80505. array(
  80506. 'name',
  80507. 'user',
  80508. 'email',
  80509. 'active',
  80510. );
  80511. foreach (array('lead', 'developer', 'contributor', 'helper') as $type) {
  80512. if (!isset($this->_packageInfo[$type])) {
  80513. continue;
  80514. }
  80515. if (isset($this->_packageInfo[$type][0])) {
  80516. foreach ($this->_packageInfo[$type] as $lead) {
  80517. $this->_stupidSchemaValidate($structure, $lead, '<' . $type . '>');
  80518. }
  80519. } else {
  80520. $this->_stupidSchemaValidate($structure, $this->_packageInfo[$type],
  80521. '<' . $type . '>');
  80522. }
  80523. }
  80524. }
  80525. function _validatePhpDep($dep, $installcondition = false)
  80526. {
  80527. $structure = array(
  80528. 'min',
  80529. '*max',
  80530. '*exclude',
  80531. );
  80532. $type = $installcondition ? '<installcondition><php>' : '<dependencies><required><php>';
  80533. $this->_stupidSchemaValidate($structure, $dep, $type);
  80534. if (isset($dep['min'])) {
  80535. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
  80536. $dep['min'])) {
  80537. $this->_invalidVersion($type . '<min>', $dep['min']);
  80538. }
  80539. }
  80540. if (isset($dep['max'])) {
  80541. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
  80542. $dep['max'])) {
  80543. $this->_invalidVersion($type . '<max>', $dep['max']);
  80544. }
  80545. }
  80546. if (isset($dep['exclude'])) {
  80547. if (!is_array($dep['exclude'])) {
  80548. $dep['exclude'] = array($dep['exclude']);
  80549. }
  80550. foreach ($dep['exclude'] as $exclude) {
  80551. if (!preg_match(
  80552. '/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?(?:-[a-zA-Z0-9]+)?\\z/',
  80553. $exclude)) {
  80554. $this->_invalidVersion($type . '<exclude>', $exclude);
  80555. }
  80556. }
  80557. }
  80558. }
  80559. function _validatePearinstallerDep($dep)
  80560. {
  80561. $structure = array(
  80562. 'min',
  80563. '*max',
  80564. '*recommended',
  80565. '*exclude',
  80566. );
  80567. $this->_stupidSchemaValidate($structure, $dep, '<dependencies><required><pearinstaller>');
  80568. if (isset($dep['min'])) {
  80569. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  80570. $dep['min'])) {
  80571. $this->_invalidVersion('<dependencies><required><pearinstaller><min>',
  80572. $dep['min']);
  80573. }
  80574. }
  80575. if (isset($dep['max'])) {
  80576. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  80577. $dep['max'])) {
  80578. $this->_invalidVersion('<dependencies><required><pearinstaller><max>',
  80579. $dep['max']);
  80580. }
  80581. }
  80582. if (isset($dep['recommended'])) {
  80583. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  80584. $dep['recommended'])) {
  80585. $this->_invalidVersion('<dependencies><required><pearinstaller><recommended>',
  80586. $dep['recommended']);
  80587. }
  80588. }
  80589. if (isset($dep['exclude'])) {
  80590. if (!is_array($dep['exclude'])) {
  80591. $dep['exclude'] = array($dep['exclude']);
  80592. }
  80593. foreach ($dep['exclude'] as $exclude) {
  80594. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  80595. $exclude)) {
  80596. $this->_invalidVersion('<dependencies><required><pearinstaller><exclude>',
  80597. $exclude);
  80598. }
  80599. }
  80600. }
  80601. }
  80602. function _validatePackageDep($dep, $group, $type = '<package>')
  80603. {
  80604. if (isset($dep['uri'])) {
  80605. if (isset($dep['conflicts'])) {
  80606. $structure = array(
  80607. 'name',
  80608. 'uri',
  80609. 'conflicts',
  80610. '*providesextension',
  80611. );
  80612. } else {
  80613. $structure = array(
  80614. 'name',
  80615. 'uri',
  80616. '*providesextension',
  80617. );
  80618. }
  80619. } else {
  80620. if (isset($dep['conflicts'])) {
  80621. $structure = array(
  80622. 'name',
  80623. 'channel',
  80624. '*min',
  80625. '*max',
  80626. '*exclude',
  80627. 'conflicts',
  80628. '*providesextension',
  80629. );
  80630. } else {
  80631. $structure = array(
  80632. 'name',
  80633. 'channel',
  80634. '*min',
  80635. '*max',
  80636. '*recommended',
  80637. '*exclude',
  80638. '*nodefault',
  80639. '*providesextension',
  80640. );
  80641. }
  80642. }
  80643. if (isset($dep['name'])) {
  80644. $type .= '<name>' . $dep['name'] . '</name>';
  80645. }
  80646. $this->_stupidSchemaValidate($structure, $dep, '<dependencies>' . $group . $type);
  80647. if (isset($dep['uri']) && (isset($dep['min']) || isset($dep['max']) ||
  80648. isset($dep['recommended']) || isset($dep['exclude']))) {
  80649. $this->_uriDepsCannotHaveVersioning('<dependencies>' . $group . $type);
  80650. }
  80651. if (isset($dep['channel']) && strtolower($dep['channel']) == '__uri') {
  80652. $this->_DepchannelCannotBeUri('<dependencies>' . $group . $type);
  80653. }
  80654. if (isset($dep['min'])) {
  80655. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  80656. $dep['min'])) {
  80657. $this->_invalidVersion('<dependencies>' . $group . $type . '<min>', $dep['min']);
  80658. }
  80659. }
  80660. if (isset($dep['max'])) {
  80661. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  80662. $dep['max'])) {
  80663. $this->_invalidVersion('<dependencies>' . $group . $type . '<max>', $dep['max']);
  80664. }
  80665. }
  80666. if (isset($dep['recommended'])) {
  80667. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  80668. $dep['recommended'])) {
  80669. $this->_invalidVersion('<dependencies>' . $group . $type . '<recommended>',
  80670. $dep['recommended']);
  80671. }
  80672. }
  80673. if (isset($dep['exclude'])) {
  80674. if (!is_array($dep['exclude'])) {
  80675. $dep['exclude'] = array($dep['exclude']);
  80676. }
  80677. foreach ($dep['exclude'] as $exclude) {
  80678. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  80679. $exclude)) {
  80680. $this->_invalidVersion('<dependencies>' . $group . $type . '<exclude>',
  80681. $exclude);
  80682. }
  80683. }
  80684. }
  80685. }
  80686. function _validateSubpackageDep($dep, $group)
  80687. {
  80688. $this->_validatePackageDep($dep, $group, '<subpackage>');
  80689. if (isset($dep['providesextension'])) {
  80690. $this->_subpackageCannotProvideExtension(isset($dep['name']) ? $dep['name'] : '');
  80691. }
  80692. if (isset($dep['conflicts'])) {
  80693. $this->_subpackagesCannotConflict(isset($dep['name']) ? $dep['name'] : '');
  80694. }
  80695. }
  80696. function _validateExtensionDep($dep, $group = false, $installcondition = false)
  80697. {
  80698. if (isset($dep['conflicts'])) {
  80699. $structure = array(
  80700. 'name',
  80701. '*min',
  80702. '*max',
  80703. '*exclude',
  80704. 'conflicts',
  80705. );
  80706. } else {
  80707. $structure = array(
  80708. 'name',
  80709. '*min',
  80710. '*max',
  80711. '*recommended',
  80712. '*exclude',
  80713. );
  80714. }
  80715. if ($installcondition) {
  80716. $type = '<installcondition><extension>';
  80717. } else {
  80718. $type = '<dependencies>' . $group . '<extension>';
  80719. }
  80720. if (isset($dep['name'])) {
  80721. $type .= '<name>' . $dep['name'] . '</name>';
  80722. }
  80723. $this->_stupidSchemaValidate($structure, $dep, $type);
  80724. if (isset($dep['min'])) {
  80725. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  80726. $dep['min'])) {
  80727. $this->_invalidVersion(substr($type, 1) . '<min', $dep['min']);
  80728. }
  80729. }
  80730. if (isset($dep['max'])) {
  80731. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  80732. $dep['max'])) {
  80733. $this->_invalidVersion(substr($type, 1) . '<max', $dep['max']);
  80734. }
  80735. }
  80736. if (isset($dep['recommended'])) {
  80737. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  80738. $dep['recommended'])) {
  80739. $this->_invalidVersion(substr($type, 1) . '<recommended', $dep['recommended']);
  80740. }
  80741. }
  80742. if (isset($dep['exclude'])) {
  80743. if (!is_array($dep['exclude'])) {
  80744. $dep['exclude'] = array($dep['exclude']);
  80745. }
  80746. foreach ($dep['exclude'] as $exclude) {
  80747. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  80748. $exclude)) {
  80749. $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
  80750. }
  80751. }
  80752. }
  80753. }
  80754. function _validateOsDep($dep, $installcondition = false)
  80755. {
  80756. $structure = array(
  80757. 'name',
  80758. '*conflicts',
  80759. );
  80760. $type = $installcondition ? '<installcondition><os>' : '<dependencies><required><os>';
  80761. if ($this->_stupidSchemaValidate($structure, $dep, $type)) {
  80762. if ($dep['name'] == '*') {
  80763. if (array_key_exists('conflicts', $dep)) {
  80764. $this->_cannotConflictWithAllOs($type);
  80765. }
  80766. }
  80767. }
  80768. }
  80769. function _validateArchDep($dep, $installcondition = false)
  80770. {
  80771. $structure = array(
  80772. 'pattern',
  80773. '*conflicts',
  80774. );
  80775. $type = $installcondition ? '<installcondition><arch>' : '<dependencies><required><arch>';
  80776. $this->_stupidSchemaValidate($structure, $dep, $type);
  80777. }
  80778. function _validateInstallConditions($cond, $release)
  80779. {
  80780. $structure = array(
  80781. '*php',
  80782. '*extension',
  80783. '*os',
  80784. '*arch',
  80785. );
  80786. if (!$this->_stupidSchemaValidate($structure,
  80787. $cond, $release)) {
  80788. return false;
  80789. }
  80790. foreach (array('php', 'extension', 'os', 'arch') as $type) {
  80791. if (isset($cond[$type])) {
  80792. $iter = $cond[$type];
  80793. if (!is_array($iter) || !isset($iter[0])) {
  80794. $iter = array($iter);
  80795. }
  80796. foreach ($iter as $package) {
  80797. if ($type == 'extension') {
  80798. $this->{"_validate{$type}Dep"}($package, false, true);
  80799. } else {
  80800. $this->{"_validate{$type}Dep"}($package, true);
  80801. }
  80802. }
  80803. }
  80804. }
  80805. }
  80806. function _validateDependencies()
  80807. {
  80808. $structure = array(
  80809. 'required',
  80810. '*optional',
  80811. '*group->name->hint'
  80812. );
  80813. if (!$this->_stupidSchemaValidate($structure,
  80814. $this->_packageInfo['dependencies'], '<dependencies>')) {
  80815. return false;
  80816. }
  80817. foreach (array('required', 'optional') as $simpledep) {
  80818. if (isset($this->_packageInfo['dependencies'][$simpledep])) {
  80819. if ($simpledep == 'optional') {
  80820. $structure = array(
  80821. '*package',
  80822. '*subpackage',
  80823. '*extension',
  80824. );
  80825. } else {
  80826. $structure = array(
  80827. 'php',
  80828. 'pearinstaller',
  80829. '*package',
  80830. '*subpackage',
  80831. '*extension',
  80832. '*os',
  80833. '*arch',
  80834. );
  80835. }
  80836. if ($this->_stupidSchemaValidate($structure,
  80837. $this->_packageInfo['dependencies'][$simpledep],
  80838. "<dependencies><$simpledep>")) {
  80839. foreach (array('package', 'subpackage', 'extension') as $type) {
  80840. if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
  80841. $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
  80842. if (!isset($iter[0])) {
  80843. $iter = array($iter);
  80844. }
  80845. foreach ($iter as $package) {
  80846. if ($type != 'extension') {
  80847. if (isset($package['uri'])) {
  80848. if (isset($package['channel'])) {
  80849. $this->_UrlOrChannel($type,
  80850. $package['name']);
  80851. }
  80852. } else {
  80853. if (!isset($package['channel'])) {
  80854. $this->_NoChannel($type, $package['name']);
  80855. }
  80856. }
  80857. }
  80858. $this->{"_validate{$type}Dep"}($package, "<$simpledep>");
  80859. }
  80860. }
  80861. }
  80862. if ($simpledep == 'optional') {
  80863. continue;
  80864. }
  80865. foreach (array('php', 'pearinstaller', 'os', 'arch') as $type) {
  80866. if (isset($this->_packageInfo['dependencies'][$simpledep][$type])) {
  80867. $iter = $this->_packageInfo['dependencies'][$simpledep][$type];
  80868. if (!isset($iter[0])) {
  80869. $iter = array($iter);
  80870. }
  80871. foreach ($iter as $package) {
  80872. $this->{"_validate{$type}Dep"}($package);
  80873. }
  80874. }
  80875. }
  80876. }
  80877. }
  80878. }
  80879. if (isset($this->_packageInfo['dependencies']['group'])) {
  80880. $groups = $this->_packageInfo['dependencies']['group'];
  80881. if (!isset($groups[0])) {
  80882. $groups = array($groups);
  80883. }
  80884. $structure = array(
  80885. '*package',
  80886. '*subpackage',
  80887. '*extension',
  80888. );
  80889. foreach ($groups as $group) {
  80890. if ($this->_stupidSchemaValidate($structure, $group, '<group>')) {
  80891. if (!PEAR_Validate::validGroupName($group['attribs']['name'])) {
  80892. $this->_invalidDepGroupName($group['attribs']['name']);
  80893. }
  80894. foreach (array('package', 'subpackage', 'extension') as $type) {
  80895. if (isset($group[$type])) {
  80896. $iter = $group[$type];
  80897. if (!isset($iter[0])) {
  80898. $iter = array($iter);
  80899. }
  80900. foreach ($iter as $package) {
  80901. if ($type != 'extension') {
  80902. if (isset($package['uri'])) {
  80903. if (isset($package['channel'])) {
  80904. $this->_UrlOrChannelGroup($type,
  80905. $package['name'],
  80906. $group['name']);
  80907. }
  80908. } else {
  80909. if (!isset($package['channel'])) {
  80910. $this->_NoChannelGroup($type,
  80911. $package['name'],
  80912. $group['name']);
  80913. }
  80914. }
  80915. }
  80916. $this->{"_validate{$type}Dep"}($package, '<group name="' .
  80917. $group['attribs']['name'] . '">');
  80918. }
  80919. }
  80920. }
  80921. }
  80922. }
  80923. }
  80924. }
  80925. function _validateCompatible()
  80926. {
  80927. $compat = $this->_packageInfo['compatible'];
  80928. if (!isset($compat[0])) {
  80929. $compat = array($compat);
  80930. }
  80931. $required = array('name', 'channel', 'min', 'max', '*exclude');
  80932. foreach ($compat as $package) {
  80933. $type = '<compatible>';
  80934. if (is_array($package) && array_key_exists('name', $package)) {
  80935. $type .= '<name>' . $package['name'] . '</name>';
  80936. }
  80937. $this->_stupidSchemaValidate($required, $package, $type);
  80938. if (is_array($package) && array_key_exists('min', $package)) {
  80939. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  80940. $package['min'])) {
  80941. $this->_invalidVersion(substr($type, 1) . '<min', $package['min']);
  80942. }
  80943. }
  80944. if (is_array($package) && array_key_exists('max', $package)) {
  80945. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  80946. $package['max'])) {
  80947. $this->_invalidVersion(substr($type, 1) . '<max', $package['max']);
  80948. }
  80949. }
  80950. if (is_array($package) && array_key_exists('exclude', $package)) {
  80951. if (!is_array($package['exclude'])) {
  80952. $package['exclude'] = array($package['exclude']);
  80953. }
  80954. foreach ($package['exclude'] as $exclude) {
  80955. if (!preg_match('/^\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?\\z/',
  80956. $exclude)) {
  80957. $this->_invalidVersion(substr($type, 1) . '<exclude', $exclude);
  80958. }
  80959. }
  80960. }
  80961. }
  80962. }
  80963. function _validateBundle($list)
  80964. {
  80965. if (!is_array($list) || !isset($list['bundledpackage'])) {
  80966. return $this->_NoBundledPackages();
  80967. }
  80968. if (!is_array($list['bundledpackage']) || !isset($list['bundledpackage'][0])) {
  80969. return $this->_AtLeast2BundledPackages();
  80970. }
  80971. foreach ($list['bundledpackage'] as $package) {
  80972. if (!is_string($package)) {
  80973. $this->_bundledPackagesMustBeFilename();
  80974. }
  80975. }
  80976. }
  80977. function _validateFilelist($list = false, $allowignore = false, $dirs = '')
  80978. {
  80979. $iscontents = false;
  80980. if (!$list) {
  80981. $iscontents = true;
  80982. $list = $this->_packageInfo['contents'];
  80983. if (isset($this->_packageInfo['bundle'])) {
  80984. return $this->_validateBundle($list);
  80985. }
  80986. }
  80987. if ($allowignore) {
  80988. $struc = array(
  80989. '*install->name->as',
  80990. '*ignore->name'
  80991. );
  80992. } else {
  80993. $struc = array(
  80994. '*dir->name->?baseinstalldir',
  80995. '*file->name->role->?baseinstalldir->?md5sum'
  80996. );
  80997. if (isset($list['dir']) && isset($list['file'])) {
  80998. // stave off validation errors without requiring a set order.
  80999. $_old = $list;
  81000. if (isset($list['attribs'])) {
  81001. $list = array('attribs' => $_old['attribs']);
  81002. }
  81003. $list['dir'] = $_old['dir'];
  81004. $list['file'] = $_old['file'];
  81005. }
  81006. }
  81007. if (!isset($list['attribs']) || !isset($list['attribs']['name'])) {
  81008. $unknown = $allowignore ? '<filelist>' : '<dir name="*unknown*">';
  81009. $dirname = $iscontents ? '<contents>' : $unknown;
  81010. } else {
  81011. $dirname = '<dir name="' . $list['attribs']['name'] . '">';
  81012. if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
  81013. str_replace('\\', '/', $list['attribs']['name']))) {
  81014. // file contains .. parent directory or . cur directory
  81015. $this->_invalidDirName($list['attribs']['name']);
  81016. }
  81017. }
  81018. $res = $this->_stupidSchemaValidate($struc, $list, $dirname);
  81019. if ($allowignore && $res) {
  81020. $ignored_or_installed = array();
  81021. $this->_pf->getFilelist();
  81022. $fcontents = $this->_pf->getContents();
  81023. $filelist = array();
  81024. if (!isset($fcontents['dir']['file'][0])) {
  81025. $fcontents['dir']['file'] = array($fcontents['dir']['file']);
  81026. }
  81027. foreach ($fcontents['dir']['file'] as $file) {
  81028. $filelist[$file['attribs']['name']] = true;
  81029. }
  81030. if (isset($list['install'])) {
  81031. if (!isset($list['install'][0])) {
  81032. $list['install'] = array($list['install']);
  81033. }
  81034. foreach ($list['install'] as $file) {
  81035. if (!isset($filelist[$file['attribs']['name']])) {
  81036. $this->_notInContents($file['attribs']['name'], 'install');
  81037. continue;
  81038. }
  81039. if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) {
  81040. $this->_multipleInstallAs($file['attribs']['name']);
  81041. }
  81042. if (!isset($ignored_or_installed[$file['attribs']['name']])) {
  81043. $ignored_or_installed[$file['attribs']['name']] = array();
  81044. }
  81045. $ignored_or_installed[$file['attribs']['name']][] = 1;
  81046. if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
  81047. str_replace('\\', '/', $file['attribs']['as']))) {
  81048. // file contains .. parent directory or . cur directory references
  81049. $this->_invalidFileInstallAs($file['attribs']['name'],
  81050. $file['attribs']['as']);
  81051. }
  81052. }
  81053. }
  81054. if (isset($list['ignore'])) {
  81055. if (!isset($list['ignore'][0])) {
  81056. $list['ignore'] = array($list['ignore']);
  81057. }
  81058. foreach ($list['ignore'] as $file) {
  81059. if (!isset($filelist[$file['attribs']['name']])) {
  81060. $this->_notInContents($file['attribs']['name'], 'ignore');
  81061. continue;
  81062. }
  81063. if (array_key_exists($file['attribs']['name'], $ignored_or_installed)) {
  81064. $this->_ignoreAndInstallAs($file['attribs']['name']);
  81065. }
  81066. }
  81067. }
  81068. }
  81069. if (!$allowignore && isset($list['file'])) {
  81070. if (is_string($list['file'])) {
  81071. $this->_oldStyleFileNotAllowed();
  81072. return false;
  81073. }
  81074. if (!isset($list['file'][0])) {
  81075. // single file
  81076. $list['file'] = array($list['file']);
  81077. }
  81078. foreach ($list['file'] as $i => $file)
  81079. {
  81080. if (isset($file['attribs']) && isset($file['attribs']['name'])) {
  81081. if ($file['attribs']['name'][0] == '.' &&
  81082. $file['attribs']['name'][1] == '/') {
  81083. // name is something like "./doc/whatever.txt"
  81084. $this->_invalidFileName($file['attribs']['name'], $dirname);
  81085. }
  81086. if (preg_match('~/\.\.?(/|\\z)|^\.\.?/~',
  81087. str_replace('\\', '/', $file['attribs']['name']))) {
  81088. // file contains .. parent directory or . cur directory
  81089. $this->_invalidFileName($file['attribs']['name'], $dirname);
  81090. }
  81091. }
  81092. if (isset($file['attribs']) && isset($file['attribs']['role'])) {
  81093. if (!$this->_validateRole($file['attribs']['role'])) {
  81094. if (isset($this->_packageInfo['usesrole'])) {
  81095. $roles = $this->_packageInfo['usesrole'];
  81096. if (!isset($roles[0])) {
  81097. $roles = array($roles);
  81098. }
  81099. foreach ($roles as $role) {
  81100. if ($role['role'] = $file['attribs']['role']) {
  81101. $msg = 'This package contains role "%role%" and requires ' .
  81102. 'package "%package%" to be used';
  81103. if (isset($role['uri'])) {
  81104. $params = array('role' => $role['role'],
  81105. 'package' => $role['uri']);
  81106. } else {
  81107. $params = array('role' => $role['role'],
  81108. 'package' => $this->_pf->_registry->
  81109. parsedPackageNameToString(array('package' =>
  81110. $role['package'], 'channel' => $role['channel']),
  81111. true));
  81112. }
  81113. $this->_stack->push('_mustInstallRole', 'error', $params, $msg);
  81114. }
  81115. }
  81116. }
  81117. $this->_invalidFileRole($file['attribs']['name'],
  81118. $dirname, $file['attribs']['role']);
  81119. }
  81120. }
  81121. if (!isset($file['attribs'])) {
  81122. continue;
  81123. }
  81124. $save = $file['attribs'];
  81125. if ($dirs) {
  81126. $save['name'] = $dirs . '/' . $save['name'];
  81127. }
  81128. unset($file['attribs']);
  81129. if (count($file) && $this->_curState != PEAR_VALIDATE_DOWNLOADING) { // has tasks
  81130. foreach ($file as $task => $value) {
  81131. if ($tagClass = $this->_pf->getTask($task)) {
  81132. if (!is_array($value) || !isset($value[0])) {
  81133. $value = array($value);
  81134. }
  81135. foreach ($value as $v) {
  81136. $ret = call_user_func(array($tagClass, 'validateXml'),
  81137. $this->_pf, $v, $this->_pf->_config, $save);
  81138. if (is_array($ret)) {
  81139. $this->_invalidTask($task, $ret, isset($save['name']) ?
  81140. $save['name'] : '');
  81141. }
  81142. }
  81143. } else {
  81144. if (isset($this->_packageInfo['usestask'])) {
  81145. $roles = $this->_packageInfo['usestask'];
  81146. if (!isset($roles[0])) {
  81147. $roles = array($roles);
  81148. }
  81149. foreach ($roles as $role) {
  81150. if ($role['task'] = $task) {
  81151. $msg = 'This package contains task "%task%" and requires ' .
  81152. 'package "%package%" to be used';
  81153. if (isset($role['uri'])) {
  81154. $params = array('task' => $role['task'],
  81155. 'package' => $role['uri']);
  81156. } else {
  81157. $params = array('task' => $role['task'],
  81158. 'package' => $this->_pf->_registry->
  81159. parsedPackageNameToString(array('package' =>
  81160. $role['package'], 'channel' => $role['channel']),
  81161. true));
  81162. }
  81163. $this->_stack->push('_mustInstallTask', 'error',
  81164. $params, $msg);
  81165. }
  81166. }
  81167. }
  81168. $this->_unknownTask($task, $save['name']);
  81169. }
  81170. }
  81171. }
  81172. }
  81173. }
  81174. if (isset($list['ignore'])) {
  81175. if (!$allowignore) {
  81176. $this->_ignoreNotAllowed('ignore');
  81177. }
  81178. }
  81179. if (isset($list['install'])) {
  81180. if (!$allowignore) {
  81181. $this->_ignoreNotAllowed('install');
  81182. }
  81183. }
  81184. if (isset($list['file'])) {
  81185. if ($allowignore) {
  81186. $this->_fileNotAllowed('file');
  81187. }
  81188. }
  81189. if (isset($list['dir'])) {
  81190. if ($allowignore) {
  81191. $this->_fileNotAllowed('dir');
  81192. } else {
  81193. if (!isset($list['dir'][0])) {
  81194. $list['dir'] = array($list['dir']);
  81195. }
  81196. foreach ($list['dir'] as $dir) {
  81197. if (isset($dir['attribs']) && isset($dir['attribs']['name'])) {
  81198. if ($dir['attribs']['name'] == '/' ||
  81199. !isset($this->_packageInfo['contents']['dir']['dir'])) {
  81200. // always use nothing if the filelist has already been flattened
  81201. $newdirs = '';
  81202. } elseif ($dirs == '') {
  81203. $newdirs = $dir['attribs']['name'];
  81204. } else {
  81205. $newdirs = $dirs . '/' . $dir['attribs']['name'];
  81206. }
  81207. } else {
  81208. $newdirs = $dirs;
  81209. }
  81210. $this->_validateFilelist($dir, $allowignore, $newdirs);
  81211. }
  81212. }
  81213. }
  81214. }
  81215. function _validateRelease()
  81216. {
  81217. if (isset($this->_packageInfo['phprelease'])) {
  81218. $release = 'phprelease';
  81219. if (isset($this->_packageInfo['providesextension'])) {
  81220. $this->_cannotProvideExtension($release);
  81221. }
  81222. if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
  81223. $this->_cannotHaveSrcpackage($release);
  81224. }
  81225. $releases = $this->_packageInfo['phprelease'];
  81226. if (!is_array($releases)) {
  81227. return true;
  81228. }
  81229. if (!isset($releases[0])) {
  81230. $releases = array($releases);
  81231. }
  81232. foreach ($releases as $rel) {
  81233. $this->_stupidSchemaValidate(array(
  81234. '*installconditions',
  81235. '*filelist',
  81236. ), $rel, '<phprelease>');
  81237. }
  81238. }
  81239. foreach (array('', 'zend') as $prefix) {
  81240. $releasetype = $prefix . 'extsrcrelease';
  81241. if (isset($this->_packageInfo[$releasetype])) {
  81242. $release = $releasetype;
  81243. if (!isset($this->_packageInfo['providesextension'])) {
  81244. $this->_mustProvideExtension($release);
  81245. }
  81246. if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
  81247. $this->_cannotHaveSrcpackage($release);
  81248. }
  81249. $releases = $this->_packageInfo[$releasetype];
  81250. if (!is_array($releases)) {
  81251. return true;
  81252. }
  81253. if (!isset($releases[0])) {
  81254. $releases = array($releases);
  81255. }
  81256. foreach ($releases as $rel) {
  81257. $this->_stupidSchemaValidate(array(
  81258. '*installconditions',
  81259. '*configureoption->name->prompt->?default',
  81260. '*binarypackage',
  81261. '*filelist',
  81262. ), $rel, '<' . $releasetype . '>');
  81263. if (isset($rel['binarypackage'])) {
  81264. if (!is_array($rel['binarypackage']) || !isset($rel['binarypackage'][0])) {
  81265. $rel['binarypackage'] = array($rel['binarypackage']);
  81266. }
  81267. foreach ($rel['binarypackage'] as $bin) {
  81268. if (!is_string($bin)) {
  81269. $this->_binaryPackageMustBePackagename();
  81270. }
  81271. }
  81272. }
  81273. }
  81274. }
  81275. $releasetype = 'extbinrelease';
  81276. if (isset($this->_packageInfo[$releasetype])) {
  81277. $release = $releasetype;
  81278. if (!isset($this->_packageInfo['providesextension'])) {
  81279. $this->_mustProvideExtension($release);
  81280. }
  81281. if (isset($this->_packageInfo['channel']) &&
  81282. !isset($this->_packageInfo['srcpackage'])) {
  81283. $this->_mustSrcPackage($release);
  81284. }
  81285. if (isset($this->_packageInfo['uri']) && !isset($this->_packageInfo['srcuri'])) {
  81286. $this->_mustSrcuri($release);
  81287. }
  81288. $releases = $this->_packageInfo[$releasetype];
  81289. if (!is_array($releases)) {
  81290. return true;
  81291. }
  81292. if (!isset($releases[0])) {
  81293. $releases = array($releases);
  81294. }
  81295. foreach ($releases as $rel) {
  81296. $this->_stupidSchemaValidate(array(
  81297. '*installconditions',
  81298. '*filelist',
  81299. ), $rel, '<' . $releasetype . '>');
  81300. }
  81301. }
  81302. }
  81303. if (isset($this->_packageInfo['bundle'])) {
  81304. $release = 'bundle';
  81305. if (isset($this->_packageInfo['providesextension'])) {
  81306. $this->_cannotProvideExtension($release);
  81307. }
  81308. if (isset($this->_packageInfo['srcpackage']) || isset($this->_packageInfo['srcuri'])) {
  81309. $this->_cannotHaveSrcpackage($release);
  81310. }
  81311. $releases = $this->_packageInfo['bundle'];
  81312. if (!is_array($releases) || !isset($releases[0])) {
  81313. $releases = array($releases);
  81314. }
  81315. foreach ($releases as $rel) {
  81316. $this->_stupidSchemaValidate(array(
  81317. '*installconditions',
  81318. '*filelist',
  81319. ), $rel, '<bundle>');
  81320. }
  81321. }
  81322. foreach ($releases as $rel) {
  81323. if (is_array($rel) && array_key_exists('installconditions', $rel)) {
  81324. $this->_validateInstallConditions($rel['installconditions'],
  81325. "<$release><installconditions>");
  81326. }
  81327. if (is_array($rel) && array_key_exists('filelist', $rel)) {
  81328. if ($rel['filelist']) {
  81329. $this->_validateFilelist($rel['filelist'], true);
  81330. }
  81331. }
  81332. }
  81333. }
  81334. /**
  81335. * This is here to allow role extension through plugins
  81336. * @param string
  81337. */
  81338. function _validateRole($role)
  81339. {
  81340. return in_array($role, PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType()));
  81341. }
  81342. function _pearVersionTooLow($version)
  81343. {
  81344. $this->_stack->push(__FUNCTION__, 'error',
  81345. array('version' => $version),
  81346. 'This package.xml requires PEAR version %version% to parse properly, we are ' .
  81347. 'version 1.10.16');
  81348. }
  81349. function _invalidTagOrder($oktags, $actual, $root)
  81350. {
  81351. $this->_stack->push(__FUNCTION__, 'error',
  81352. array('oktags' => $oktags, 'actual' => $actual, 'root' => $root),
  81353. 'Invalid tag order in %root%, found <%actual%> expected one of "%oktags%"');
  81354. }
  81355. function _ignoreNotAllowed($type)
  81356. {
  81357. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
  81358. '<%type%> is not allowed inside global <contents>, only inside ' .
  81359. '<phprelease>/<extbinrelease>/<zendextbinrelease>, use <dir> and <file> only');
  81360. }
  81361. function _fileNotAllowed($type)
  81362. {
  81363. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
  81364. '<%type%> is not allowed inside release <filelist>, only inside ' .
  81365. '<contents>, use <ignore> and <install> only');
  81366. }
  81367. function _oldStyleFileNotAllowed()
  81368. {
  81369. $this->_stack->push(__FUNCTION__, 'error', array(),
  81370. 'Old-style <file>name</file> is not allowed. Use' .
  81371. '<file name="name" role="role"/>');
  81372. }
  81373. function _tagMissingAttribute($tag, $attr, $context)
  81374. {
  81375. $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag,
  81376. 'attribute' => $attr, 'context' => $context),
  81377. 'tag <%tag%> in context "%context%" has no attribute "%attribute%"');
  81378. }
  81379. function _tagHasNoAttribs($tag, $context)
  81380. {
  81381. $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag,
  81382. 'context' => $context),
  81383. 'tag <%tag%> has no attributes in context "%context%"');
  81384. }
  81385. function _invalidInternalStructure()
  81386. {
  81387. $this->_stack->push(__FUNCTION__, 'exception', array(),
  81388. 'internal array was not generated by compatible parser, or extreme parser error, cannot continue');
  81389. }
  81390. function _invalidFileRole($file, $dir, $role)
  81391. {
  81392. $this->_stack->push(__FUNCTION__, 'error', array(
  81393. 'file' => $file, 'dir' => $dir, 'role' => $role,
  81394. 'roles' => PEAR_Installer_Role::getValidRoles($this->_pf->getPackageType())),
  81395. 'File "%file%" in directory "%dir%" has invalid role "%role%", should be one of %roles%');
  81396. }
  81397. function _invalidFileName($file, $dir)
  81398. {
  81399. $this->_stack->push(__FUNCTION__, 'error', array(
  81400. 'file' => $file),
  81401. 'File "%file%" in directory "%dir%" cannot begin with "./" or contain ".."');
  81402. }
  81403. function _invalidFileInstallAs($file, $as)
  81404. {
  81405. $this->_stack->push(__FUNCTION__, 'error', array(
  81406. 'file' => $file, 'as' => $as),
  81407. 'File "%file%" <install as="%as%"/> cannot contain "./" or contain ".."');
  81408. }
  81409. function _invalidDirName($dir)
  81410. {
  81411. $this->_stack->push(__FUNCTION__, 'error', array(
  81412. 'dir' => $file),
  81413. 'Directory "%dir%" cannot begin with "./" or contain ".."');
  81414. }
  81415. function _filelistCannotContainFile($filelist)
  81416. {
  81417. $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist),
  81418. '<%tag%> can only contain <dir>, contains <file>. Use ' .
  81419. '<dir name="/"> as the first dir element');
  81420. }
  81421. function _filelistMustContainDir($filelist)
  81422. {
  81423. $this->_stack->push(__FUNCTION__, 'error', array('tag' => $filelist),
  81424. '<%tag%> must contain <dir>. Use <dir name="/"> as the ' .
  81425. 'first dir element');
  81426. }
  81427. function _tagCannotBeEmpty($tag)
  81428. {
  81429. $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag),
  81430. '<%tag%> cannot be empty (<%tag%/>)');
  81431. }
  81432. function _UrlOrChannel($type, $name)
  81433. {
  81434. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
  81435. 'name' => $name),
  81436. 'Required dependency <%type%> "%name%" can have either url OR ' .
  81437. 'channel attributes, and not both');
  81438. }
  81439. function _NoChannel($type, $name)
  81440. {
  81441. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
  81442. 'name' => $name),
  81443. 'Required dependency <%type%> "%name%" must have either url OR ' .
  81444. 'channel attributes');
  81445. }
  81446. function _UrlOrChannelGroup($type, $name, $group)
  81447. {
  81448. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
  81449. 'name' => $name, 'group' => $group),
  81450. 'Group "%group%" dependency <%type%> "%name%" can have either url OR ' .
  81451. 'channel attributes, and not both');
  81452. }
  81453. function _NoChannelGroup($type, $name, $group)
  81454. {
  81455. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type,
  81456. 'name' => $name, 'group' => $group),
  81457. 'Group "%group%" dependency <%type%> "%name%" must have either url OR ' .
  81458. 'channel attributes');
  81459. }
  81460. function _unknownChannel($channel)
  81461. {
  81462. $this->_stack->push(__FUNCTION__, 'error', array('channel' => $channel),
  81463. 'Unknown channel "%channel%"');
  81464. }
  81465. function _noPackageVersion()
  81466. {
  81467. $this->_stack->push(__FUNCTION__, 'error', array(),
  81468. 'package.xml <package> tag has no version attribute, or version is not 2.0');
  81469. }
  81470. function _NoBundledPackages()
  81471. {
  81472. $this->_stack->push(__FUNCTION__, 'error', array(),
  81473. 'No <bundledpackage> tag was found in <contents>, required for bundle packages');
  81474. }
  81475. function _AtLeast2BundledPackages()
  81476. {
  81477. $this->_stack->push(__FUNCTION__, 'error', array(),
  81478. 'At least 2 packages must be bundled in a bundle package');
  81479. }
  81480. function _ChannelOrUri($name)
  81481. {
  81482. $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
  81483. 'Bundled package "%name%" can have either a uri or a channel, not both');
  81484. }
  81485. function _noChildTag($child, $tag)
  81486. {
  81487. $this->_stack->push(__FUNCTION__, 'error', array('child' => $child, 'tag' => $tag),
  81488. 'Tag <%tag%> is missing child tag <%child%>');
  81489. }
  81490. function _invalidVersion($type, $value)
  81491. {
  81492. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value),
  81493. 'Version type <%type%> is not a valid version (%value%)');
  81494. }
  81495. function _invalidState($type, $value)
  81496. {
  81497. $states = array('stable', 'beta', 'alpha', 'devel');
  81498. if ($type != 'api') {
  81499. $states[] = 'snapshot';
  81500. }
  81501. if (strtolower($value) == 'rc') {
  81502. $this->_stack->push(__FUNCTION__, 'error',
  81503. array('version' => $this->_packageInfo['version']['release']),
  81504. 'RC is not a state, it is a version postfix, try %version%RC1, stability beta');
  81505. }
  81506. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type, 'value' => $value,
  81507. 'types' => $states),
  81508. 'Stability type <%type%> is not a valid stability (%value%), must be one of ' .
  81509. '%types%');
  81510. }
  81511. function _invalidTask($task, $ret, $file)
  81512. {
  81513. switch ($ret[0]) {
  81514. case PEAR_TASK_ERROR_MISSING_ATTRIB :
  81515. $info = array('attrib' => $ret[1], 'task' => $task, 'file' => $file);
  81516. $msg = 'task <%task%> is missing attribute "%attrib%" in file %file%';
  81517. break;
  81518. case PEAR_TASK_ERROR_NOATTRIBS :
  81519. $info = array('task' => $task, 'file' => $file);
  81520. $msg = 'task <%task%> has no attributes in file %file%';
  81521. break;
  81522. case PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE :
  81523. $info = array('attrib' => $ret[1], 'values' => $ret[3],
  81524. 'was' => $ret[2], 'task' => $task, 'file' => $file);
  81525. $msg = 'task <%task%> attribute "%attrib%" has the wrong value "%was%" '.
  81526. 'in file %file%, expecting one of "%values%"';
  81527. break;
  81528. case PEAR_TASK_ERROR_INVALID :
  81529. $info = array('reason' => $ret[1], 'task' => $task, 'file' => $file);
  81530. $msg = 'task <%task%> in file %file% is invalid because of "%reason%"';
  81531. break;
  81532. }
  81533. $this->_stack->push(__FUNCTION__, 'error', $info, $msg);
  81534. }
  81535. function _unknownTask($task, $file)
  81536. {
  81537. $this->_stack->push(__FUNCTION__, 'error', array('task' => $task, 'file' => $file),
  81538. 'Unknown task "%task%" passed in file <file name="%file%">');
  81539. }
  81540. function _subpackageCannotProvideExtension($name)
  81541. {
  81542. $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
  81543. 'Subpackage dependency "%name%" cannot use <providesextension>, ' .
  81544. 'only package dependencies can use this tag');
  81545. }
  81546. function _subpackagesCannotConflict($name)
  81547. {
  81548. $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
  81549. 'Subpackage dependency "%name%" cannot use <conflicts/>, ' .
  81550. 'only package dependencies can use this tag');
  81551. }
  81552. function _cannotProvideExtension($release)
  81553. {
  81554. $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
  81555. '<%release%> packages cannot use <providesextension>, only extbinrelease, extsrcrelease, zendextsrcrelease, and zendextbinrelease can provide a PHP extension');
  81556. }
  81557. function _mustProvideExtension($release)
  81558. {
  81559. $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
  81560. '<%release%> packages must use <providesextension> to indicate which PHP extension is provided');
  81561. }
  81562. function _cannotHaveSrcpackage($release)
  81563. {
  81564. $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
  81565. '<%release%> packages cannot specify a source code package, only extension binaries may use the <srcpackage> tag');
  81566. }
  81567. function _mustSrcPackage($release)
  81568. {
  81569. $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
  81570. '<extbinrelease>/<zendextbinrelease> packages must specify a source code package with <srcpackage>');
  81571. }
  81572. function _mustSrcuri($release)
  81573. {
  81574. $this->_stack->push(__FUNCTION__, 'error', array('release' => $release),
  81575. '<extbinrelease>/<zendextbinrelease> packages must specify a source code package with <srcuri>');
  81576. }
  81577. function _uriDepsCannotHaveVersioning($type)
  81578. {
  81579. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
  81580. '%type%: dependencies with a <uri> tag cannot have any versioning information');
  81581. }
  81582. function _conflictingDepsCannotHaveVersioning($type)
  81583. {
  81584. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
  81585. '%type%: conflicting dependencies cannot have versioning info, use <exclude> to ' .
  81586. 'exclude specific versions of a dependency');
  81587. }
  81588. function _DepchannelCannotBeUri($type)
  81589. {
  81590. $this->_stack->push(__FUNCTION__, 'error', array('type' => $type),
  81591. '%type%: channel cannot be __uri, this is a pseudo-channel reserved for uri ' .
  81592. 'dependencies only');
  81593. }
  81594. function _bundledPackagesMustBeFilename()
  81595. {
  81596. $this->_stack->push(__FUNCTION__, 'error', array(),
  81597. '<bundledpackage> tags must contain only the filename of a package release ' .
  81598. 'in the bundle');
  81599. }
  81600. function _binaryPackageMustBePackagename()
  81601. {
  81602. $this->_stack->push(__FUNCTION__, 'error', array(),
  81603. '<binarypackage> tags must contain the name of a package that is ' .
  81604. 'a compiled version of this extsrc/zendextsrc package');
  81605. }
  81606. function _fileNotFound($file)
  81607. {
  81608. $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
  81609. 'File "%file%" in package.xml does not exist');
  81610. }
  81611. function _notInContents($file, $tag)
  81612. {
  81613. $this->_stack->push(__FUNCTION__, 'error', array('file' => $file, 'tag' => $tag),
  81614. '<%tag% name="%file%"> is invalid, file is not in <contents>');
  81615. }
  81616. function _cannotValidateNoPathSet()
  81617. {
  81618. $this->_stack->push(__FUNCTION__, 'error', array(),
  81619. 'Cannot validate files, no path to package file is set (use setPackageFile())');
  81620. }
  81621. function _usesroletaskMustHaveChannelOrUri($role, $tag)
  81622. {
  81623. $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag),
  81624. '<%tag%> for role "%role%" must contain either <uri>, or <channel> and <package>');
  81625. }
  81626. function _usesroletaskMustHavePackage($role, $tag)
  81627. {
  81628. $this->_stack->push(__FUNCTION__, 'error', array('role' => $role, 'tag' => $tag),
  81629. '<%tag%> for role "%role%" must contain <package>');
  81630. }
  81631. function _usesroletaskMustHaveRoleTask($tag, $type)
  81632. {
  81633. $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag, 'type' => $type),
  81634. '<%tag%> must contain <%type%> defining the %type% to be used');
  81635. }
  81636. function _cannotConflictWithAllOs($type)
  81637. {
  81638. $this->_stack->push(__FUNCTION__, 'error', array('tag' => $tag),
  81639. '%tag% cannot conflict with all OSes');
  81640. }
  81641. function _invalidDepGroupName($name)
  81642. {
  81643. $this->_stack->push(__FUNCTION__, 'error', array('name' => $name),
  81644. 'Invalid dependency group name "%name%"');
  81645. }
  81646. function _multipleToplevelDirNotAllowed()
  81647. {
  81648. $this->_stack->push(__FUNCTION__, 'error', array(),
  81649. 'Multiple top-level <dir> tags are not allowed. Enclose them ' .
  81650. 'in a <dir name="/">');
  81651. }
  81652. function _multipleInstallAs($file)
  81653. {
  81654. $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
  81655. 'Only one <install> tag is allowed for file "%file%"');
  81656. }
  81657. function _ignoreAndInstallAs($file)
  81658. {
  81659. $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
  81660. 'Cannot have both <ignore> and <install> tags for file "%file%"');
  81661. }
  81662. function _analyzeBundledPackages()
  81663. {
  81664. if (!$this->_isValid) {
  81665. return false;
  81666. }
  81667. if (!$this->_pf->getPackageType() == 'bundle') {
  81668. return false;
  81669. }
  81670. if (!isset($this->_pf->_packageFile)) {
  81671. return false;
  81672. }
  81673. $dir_prefix = dirname($this->_pf->_packageFile);
  81674. $common = new PEAR_Common;
  81675. $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') :
  81676. array($common, 'log');
  81677. $info = $this->_pf->getContents();
  81678. $info = $info['bundledpackage'];
  81679. if (!is_array($info)) {
  81680. $info = array($info);
  81681. }
  81682. $pkg = new PEAR_PackageFile($this->_pf->_config);
  81683. foreach ($info as $package) {
  81684. if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $package)) {
  81685. $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $package);
  81686. $this->_isValid = 0;
  81687. continue;
  81688. }
  81689. call_user_func_array($log, array(1, "Analyzing bundled package $package"));
  81690. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  81691. $ret = $pkg->fromAnyFile($dir_prefix . DIRECTORY_SEPARATOR . $package,
  81692. PEAR_VALIDATE_NORMAL);
  81693. PEAR::popErrorHandling();
  81694. if (PEAR::isError($ret)) {
  81695. call_user_func_array($log, array(0, "ERROR: package $package is not a valid " .
  81696. 'package'));
  81697. $inf = $ret->getUserInfo();
  81698. if (is_array($inf)) {
  81699. foreach ($inf as $err) {
  81700. call_user_func_array($log, array(1, $err['message']));
  81701. }
  81702. }
  81703. return false;
  81704. }
  81705. }
  81706. return true;
  81707. }
  81708. function _analyzePhpFiles()
  81709. {
  81710. if (!$this->_isValid) {
  81711. return false;
  81712. }
  81713. if (!isset($this->_pf->_packageFile)) {
  81714. $this->_cannotValidateNoPathSet();
  81715. return false;
  81716. }
  81717. $dir_prefix = dirname($this->_pf->_packageFile);
  81718. $common = new PEAR_Common;
  81719. $log = isset($this->_pf->_logger) ? array(&$this->_pf->_logger, 'log') :
  81720. array(&$common, 'log');
  81721. $info = $this->_pf->getContents();
  81722. if (!$info || !isset($info['dir']['file'])) {
  81723. $this->_tagCannotBeEmpty('contents><dir');
  81724. return false;
  81725. }
  81726. $info = $info['dir']['file'];
  81727. if (isset($info['attribs'])) {
  81728. $info = array($info);
  81729. }
  81730. $provides = array();
  81731. foreach ($info as $fa) {
  81732. $fa = $fa['attribs'];
  81733. $file = $fa['name'];
  81734. if (!file_exists($dir_prefix . DIRECTORY_SEPARATOR . $file)) {
  81735. $this->_fileNotFound($dir_prefix . DIRECTORY_SEPARATOR . $file);
  81736. $this->_isValid = 0;
  81737. continue;
  81738. }
  81739. if (in_array($fa['role'], PEAR_Installer_Role::getPhpRoles()) && $dir_prefix) {
  81740. call_user_func_array($log, array(1, "Analyzing $file"));
  81741. $srcinfo = $this->analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
  81742. if ($srcinfo) {
  81743. $provides = array_merge($provides, $this->_buildProvidesArray($srcinfo));
  81744. }
  81745. }
  81746. }
  81747. $this->_packageName = $pn = $this->_pf->getPackage();
  81748. $pnl = strlen($pn);
  81749. foreach ($provides as $key => $what) {
  81750. if (isset($what['explicit']) || !$what) {
  81751. // skip conformance checks if the provides entry is
  81752. // specified in the package.xml file
  81753. continue;
  81754. }
  81755. extract($what);
  81756. if ($type == 'class') {
  81757. if (!strncasecmp($name, $pn, $pnl)) {
  81758. continue;
  81759. }
  81760. $this->_stack->push(__FUNCTION__, 'warning',
  81761. array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn),
  81762. 'in %file%: %type% "%name%" not prefixed with package name "%package%"');
  81763. } elseif ($type == 'function') {
  81764. if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
  81765. continue;
  81766. }
  81767. $this->_stack->push(__FUNCTION__, 'warning',
  81768. array('file' => $file, 'type' => $type, 'name' => $name, 'package' => $pn),
  81769. 'in %file%: %type% "%name%" not prefixed with package name "%package%"');
  81770. }
  81771. }
  81772. return $this->_isValid;
  81773. }
  81774. /**
  81775. * Analyze the source code of the given PHP file
  81776. *
  81777. * @param string Filename of the PHP file
  81778. * @param boolean whether to analyze $file as the file contents
  81779. * @return mixed
  81780. */
  81781. function analyzeSourceCode($file, $string = false)
  81782. {
  81783. if (!function_exists("token_get_all")) {
  81784. $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
  81785. 'Parser error: token_get_all() function must exist to analyze source code, PHP may have been compiled with --disable-tokenizer');
  81786. return false;
  81787. }
  81788. if (!defined('T_DOC_COMMENT')) {
  81789. define('T_DOC_COMMENT', T_COMMENT);
  81790. }
  81791. if (!defined('T_INTERFACE')) {
  81792. define('T_INTERFACE', -1);
  81793. }
  81794. if (!defined('T_IMPLEMENTS')) {
  81795. define('T_IMPLEMENTS', -1);
  81796. }
  81797. if ($string) {
  81798. $contents = $file;
  81799. } else {
  81800. if (!$fp = @fopen($file, "r")) {
  81801. return false;
  81802. }
  81803. fclose($fp);
  81804. $contents = file_get_contents($file);
  81805. }
  81806. // Silence this function so we can catch PHP Warnings and show our own custom message
  81807. $tokens = @token_get_all($contents);
  81808. if (isset($php_errormsg)) {
  81809. if (isset($this->_stack)) {
  81810. $pn = $this->_pf->getPackage();
  81811. $this->_stack->push(__FUNCTION__, 'warning',
  81812. array('file' => $file, 'package' => $pn),
  81813. 'in %file%: Could not process file for unknown reasons,' .
  81814. ' possibly a PHP parse error in %file% from %package%');
  81815. }
  81816. }
  81817. /*
  81818. for ($i = 0; $i < sizeof($tokens); $i++) {
  81819. @list($token, $data) = $tokens[$i];
  81820. if (is_string($token)) {
  81821. var_dump($token);
  81822. } else {
  81823. print token_name($token) . ' ';
  81824. var_dump(rtrim($data));
  81825. }
  81826. }
  81827. */
  81828. $look_for = 0;
  81829. $paren_level = 0;
  81830. $bracket_level = 0;
  81831. $brace_level = 0;
  81832. $lastphpdoc = '';
  81833. $current_class = '';
  81834. $current_interface = '';
  81835. $current_class_level = -1;
  81836. $current_function = '';
  81837. $current_function_level = -1;
  81838. $declared_classes = array();
  81839. $declared_interfaces = array();
  81840. $declared_functions = array();
  81841. $declared_methods = array();
  81842. $used_classes = array();
  81843. $used_functions = array();
  81844. $extends = array();
  81845. $implements = array();
  81846. $nodeps = array();
  81847. $inquote = false;
  81848. $interface = false;
  81849. for ($i = 0; $i < sizeof($tokens); $i++) {
  81850. if (is_array($tokens[$i])) {
  81851. list($token, $data) = $tokens[$i];
  81852. } else {
  81853. $token = $tokens[$i];
  81854. $data = '';
  81855. }
  81856. if ($inquote) {
  81857. if ($token != '"' && $token != T_END_HEREDOC) {
  81858. continue;
  81859. } else {
  81860. $inquote = false;
  81861. continue;
  81862. }
  81863. }
  81864. switch ($token) {
  81865. case T_WHITESPACE :
  81866. continue 2;
  81867. case ';':
  81868. if ($interface) {
  81869. $current_function = '';
  81870. $current_function_level = -1;
  81871. }
  81872. break;
  81873. case '"':
  81874. case T_START_HEREDOC:
  81875. $inquote = true;
  81876. break;
  81877. case T_CURLY_OPEN:
  81878. case T_DOLLAR_OPEN_CURLY_BRACES:
  81879. case '{': $brace_level++; continue 2;
  81880. case '}':
  81881. $brace_level--;
  81882. if ($current_class_level == $brace_level) {
  81883. $current_class = '';
  81884. $current_class_level = -1;
  81885. }
  81886. if ($current_function_level == $brace_level) {
  81887. $current_function = '';
  81888. $current_function_level = -1;
  81889. }
  81890. continue 2;
  81891. case '[': $bracket_level++; continue 2;
  81892. case ']': $bracket_level--; continue 2;
  81893. case '(': $paren_level++; continue 2;
  81894. case ')': $paren_level--; continue 2;
  81895. case T_INTERFACE:
  81896. $interface = true;
  81897. case T_CLASS:
  81898. if (($current_class_level != -1) || ($current_function_level != -1)) {
  81899. if (isset($this->_stack)) {
  81900. $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
  81901. 'Parser error: invalid PHP found in file "%file%"');
  81902. } else {
  81903. PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"",
  81904. PEAR_COMMON_ERROR_INVALIDPHP);
  81905. }
  81906. return false;
  81907. }
  81908. case T_FUNCTION:
  81909. case T_NEW:
  81910. case T_EXTENDS:
  81911. case T_IMPLEMENTS:
  81912. $look_for = $token;
  81913. continue 2;
  81914. case T_STRING:
  81915. if ($look_for == T_CLASS) {
  81916. $current_class = $data;
  81917. $current_class_level = $brace_level;
  81918. $declared_classes[] = $current_class;
  81919. } elseif ($look_for == T_INTERFACE) {
  81920. $current_interface = $data;
  81921. $current_class_level = $brace_level;
  81922. $declared_interfaces[] = $current_interface;
  81923. } elseif ($look_for == T_IMPLEMENTS) {
  81924. $implements[$current_class] = $data;
  81925. } elseif ($look_for == T_EXTENDS) {
  81926. $extends[$current_class] = $data;
  81927. } elseif ($look_for == T_FUNCTION) {
  81928. if ($current_class) {
  81929. $current_function = "$current_class::$data";
  81930. $declared_methods[$current_class][] = $data;
  81931. } elseif ($current_interface) {
  81932. $current_function = "$current_interface::$data";
  81933. $declared_methods[$current_interface][] = $data;
  81934. } else {
  81935. $current_function = $data;
  81936. $declared_functions[] = $current_function;
  81937. }
  81938. $current_function_level = $brace_level;
  81939. $m = array();
  81940. } elseif ($look_for == T_NEW) {
  81941. $used_classes[$data] = true;
  81942. }
  81943. $look_for = 0;
  81944. continue 2;
  81945. case T_VARIABLE:
  81946. $look_for = 0;
  81947. continue 2;
  81948. case T_DOC_COMMENT:
  81949. case T_COMMENT:
  81950. if (preg_match('!^/\*\*\s!', $data)) {
  81951. $lastphpdoc = $data;
  81952. if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
  81953. $nodeps = array_merge($nodeps, $m[1]);
  81954. }
  81955. }
  81956. continue 2;
  81957. case T_DOUBLE_COLON:
  81958. $token = $tokens[$i - 1][0];
  81959. if (!($token == T_WHITESPACE || $token == T_STRING || $token == T_STATIC || $token == T_VARIABLE)) {
  81960. if (isset($this->_stack)) {
  81961. $this->_stack->push(__FUNCTION__, 'warning', array('file' => $file),
  81962. 'Parser error: invalid PHP found in file "%file%"');
  81963. } else {
  81964. PEAR::raiseError("Parser error: invalid PHP found in file \"$file\"",
  81965. PEAR_COMMON_ERROR_INVALIDPHP);
  81966. }
  81967. return false;
  81968. }
  81969. $class = $tokens[$i - 1][1];
  81970. if (strtolower($class) != 'parent') {
  81971. $used_classes[$class] = true;
  81972. }
  81973. continue 2;
  81974. }
  81975. }
  81976. return array(
  81977. "source_file" => $file,
  81978. "declared_classes" => $declared_classes,
  81979. "declared_interfaces" => $declared_interfaces,
  81980. "declared_methods" => $declared_methods,
  81981. "declared_functions" => $declared_functions,
  81982. "used_classes" => array_diff(array_keys($used_classes), $nodeps),
  81983. "inheritance" => $extends,
  81984. "implements" => $implements,
  81985. );
  81986. }
  81987. /**
  81988. * Build a "provides" array from data returned by
  81989. * analyzeSourceCode(). The format of the built array is like
  81990. * this:
  81991. *
  81992. * array(
  81993. * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
  81994. * ...
  81995. * )
  81996. *
  81997. *
  81998. * @param array $srcinfo array with information about a source file
  81999. * as returned by the analyzeSourceCode() method.
  82000. *
  82001. * @return void
  82002. *
  82003. * @access private
  82004. *
  82005. */
  82006. function _buildProvidesArray($srcinfo)
  82007. {
  82008. if (!$this->_isValid) {
  82009. return array();
  82010. }
  82011. $providesret = array();
  82012. $file = basename($srcinfo['source_file']);
  82013. $pn = isset($this->_pf) ? $this->_pf->getPackage() : '';
  82014. $pnl = strlen($pn);
  82015. foreach ($srcinfo['declared_classes'] as $class) {
  82016. $key = "class;$class";
  82017. if (isset($providesret[$key])) {
  82018. continue;
  82019. }
  82020. $providesret[$key] =
  82021. array('file'=> $file, 'type' => 'class', 'name' => $class);
  82022. if (isset($srcinfo['inheritance'][$class])) {
  82023. $providesret[$key]['extends'] =
  82024. $srcinfo['inheritance'][$class];
  82025. }
  82026. }
  82027. foreach ($srcinfo['declared_methods'] as $class => $methods) {
  82028. foreach ($methods as $method) {
  82029. $function = "$class::$method";
  82030. $key = "function;$function";
  82031. if ($method[0] == '_' || !strcasecmp($method, $class) ||
  82032. isset($providesret[$key])) {
  82033. continue;
  82034. }
  82035. $providesret[$key] =
  82036. array('file'=> $file, 'type' => 'function', 'name' => $function);
  82037. }
  82038. }
  82039. foreach ($srcinfo['declared_functions'] as $function) {
  82040. $key = "function;$function";
  82041. if ($function[0] == '_' || isset($providesret[$key])) {
  82042. continue;
  82043. }
  82044. if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
  82045. $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
  82046. }
  82047. $providesret[$key] =
  82048. array('file'=> $file, 'type' => 'function', 'name' => $function);
  82049. }
  82050. return $providesret;
  82051. }
  82052. }
  82053. <?php
  82054. /**
  82055. * PEAR_Proxy
  82056. *
  82057. * HTTP Proxy handling
  82058. *
  82059. * @category pear
  82060. * @package PEAR
  82061. * @author Nico Boehr
  82062. * @copyright 1997-2009 The Authors
  82063. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  82064. * @link http://pear.php.net/package/PEAR
  82065. */
  82066. class PEAR_Proxy
  82067. {
  82068. var $config = null;
  82069. /**
  82070. * @access private
  82071. */
  82072. var $proxy_host;
  82073. /**
  82074. * @access private
  82075. */
  82076. var $proxy_port;
  82077. /**
  82078. * @access private
  82079. */
  82080. var $proxy_user;
  82081. /**
  82082. * @access private
  82083. */
  82084. var $proxy_pass;
  82085. /**
  82086. * @access private
  82087. */
  82088. var $proxy_schema;
  82089. function __construct($config = null)
  82090. {
  82091. $this->config = $config;
  82092. $this->_parseProxyInfo();
  82093. }
  82094. /**
  82095. * @access private
  82096. */
  82097. function _parseProxyInfo()
  82098. {
  82099. $this->proxy_host = $this->proxy_port = $this->proxy_user = $this->proxy_pass = '';
  82100. if ($this->config->get('http_proxy')&&
  82101. $proxy = parse_url($this->config->get('http_proxy'))
  82102. ) {
  82103. $this->proxy_host = isset($proxy['host']) ? $proxy['host'] : null;
  82104. $this->proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080;
  82105. $this->proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null;
  82106. $this->proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;
  82107. $this->proxy_schema = (isset($proxy['scheme']) && $proxy['scheme'] == 'https') ? 'https' : 'http';
  82108. }
  82109. }
  82110. /**
  82111. * @access private
  82112. */
  82113. function _httpConnect($fp, $host, $port)
  82114. {
  82115. fwrite($fp, "CONNECT $host:$port HTTP/1.1\r\n");
  82116. fwrite($fp, "Host: $host:$port\r\n");
  82117. if ($this->getProxyAuth()) {
  82118. fwrite($fp, 'Proxy-Authorization: Basic ' . $this->getProxyAuth() . "\r\n");
  82119. }
  82120. fwrite($fp, "\r\n");
  82121. while ($line = trim(fgets($fp, 1024))) {
  82122. if (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
  82123. $code = (int)$matches[1];
  82124. /* as per RFC 2817 */
  82125. if ($code < 200 || $code >= 300) {
  82126. return PEAR::raiseError("Establishing a CONNECT tunnel through proxy failed with response code $code");
  82127. }
  82128. }
  82129. }
  82130. // connection was successful -- establish SSL through
  82131. // the tunnel
  82132. $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
  82133. if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
  82134. $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
  82135. $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
  82136. }
  82137. // set the correct hostname for working hostname
  82138. // verification
  82139. stream_context_set_option($fp, 'ssl', 'peer_name', $host);
  82140. // blocking socket needed for
  82141. // stream_socket_enable_crypto()
  82142. // see
  82143. // <http://php.net/manual/en/function.stream-socket-enable-crypto.php>
  82144. stream_set_blocking ($fp, true);
  82145. $crypto_res = stream_socket_enable_crypto($fp, true, $crypto_method);
  82146. if (!$crypto_res) {
  82147. return PEAR::raiseError("Could not establish SSL connection through proxy: $crypto_res");
  82148. }
  82149. return true;
  82150. }
  82151. /**
  82152. * get the authorization information for the proxy, encoded to be
  82153. * passed in the Proxy-Authentication HTTP header.
  82154. * @return null|string the encoded authentication information if a
  82155. * proxy and authentication is configured, null
  82156. * otherwise.
  82157. */
  82158. function getProxyAuth()
  82159. {
  82160. if ($this->isProxyConfigured() && $this->proxy_user != '') {
  82161. return base64_encode($this->proxy_user . ':' . $this->proxy_pass);
  82162. }
  82163. return null;
  82164. }
  82165. function getProxyUser()
  82166. {
  82167. return $this->proxy_user;
  82168. }
  82169. /**
  82170. * Check if we are configured to use a proxy.
  82171. *
  82172. * @return boolean true if we are configured to use a proxy, false
  82173. * otherwise.
  82174. * @access public
  82175. */
  82176. function isProxyConfigured()
  82177. {
  82178. return $this->proxy_host != '';
  82179. }
  82180. /**
  82181. * Open a socket to a remote server, possibly involving a HTTP
  82182. * proxy.
  82183. *
  82184. * If an HTTP proxy has been configured (http_proxy PEAR_Config
  82185. * setting), the proxy will be used.
  82186. *
  82187. * @param string $host the host to connect to
  82188. * @param string $port the port to connect to
  82189. * @param boolean $secure if true, establish a secure connection
  82190. * using TLS.
  82191. * @access public
  82192. */
  82193. function openSocket($host, $port, $secure = false)
  82194. {
  82195. if ($this->isProxyConfigured()) {
  82196. $fp = @fsockopen(
  82197. $this->proxy_host, $this->proxy_port,
  82198. $errno, $errstr, 15
  82199. );
  82200. if (!$fp) {
  82201. return PEAR::raiseError("Connection to the proxy failed: $errstr", -9276);
  82202. }
  82203. /* HTTPS is to be used and we have a proxy, use CONNECT verb */
  82204. if ($secure) {
  82205. $res = $this->_httpConnect($fp, $host, $port);
  82206. if (PEAR::isError($res)) {
  82207. return $res;
  82208. }
  82209. }
  82210. } else {
  82211. if ($secure) {
  82212. $host = 'ssl://' . $host;
  82213. }
  82214. $fp = @fsockopen($host, $port, $errno, $errstr);
  82215. if (!$fp) {
  82216. return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
  82217. }
  82218. }
  82219. return $fp;
  82220. }
  82221. }
  82222. <?php
  82223. /**
  82224. * PEAR_Registry
  82225. *
  82226. * PHP versions 4 and 5
  82227. *
  82228. * @category pear
  82229. * @package PEAR
  82230. * @author Stig Bakken <ssb@php.net>
  82231. * @author Tomas V. V. Cox <cox@idecnet.com>
  82232. * @author Greg Beaver <cellog@php.net>
  82233. * @copyright 1997-2009 The Authors
  82234. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  82235. * @link http://pear.php.net/package/PEAR
  82236. * @since File available since Release 0.1
  82237. */
  82238. /**
  82239. * for PEAR_Error
  82240. */
  82241. require_once 'phar://go-pear.phar/' . 'PEAR.php';
  82242. require_once 'phar://go-pear.phar/' . 'PEAR/DependencyDB.php';
  82243. define('PEAR_REGISTRY_ERROR_LOCK', -2);
  82244. define('PEAR_REGISTRY_ERROR_FORMAT', -3);
  82245. define('PEAR_REGISTRY_ERROR_FILE', -4);
  82246. define('PEAR_REGISTRY_ERROR_CONFLICT', -5);
  82247. define('PEAR_REGISTRY_ERROR_CHANNEL_FILE', -6);
  82248. /**
  82249. * Administration class used to maintain the installed package database.
  82250. * @category pear
  82251. * @package PEAR
  82252. * @author Stig Bakken <ssb@php.net>
  82253. * @author Tomas V. V. Cox <cox@idecnet.com>
  82254. * @author Greg Beaver <cellog@php.net>
  82255. * @copyright 1997-2009 The Authors
  82256. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  82257. * @version Release: 1.10.16
  82258. * @link http://pear.php.net/package/PEAR
  82259. * @since Class available since Release 1.4.0a1
  82260. */
  82261. class PEAR_Registry extends PEAR
  82262. {
  82263. /**
  82264. * File containing all channel information.
  82265. * @var string
  82266. */
  82267. var $channels = '';
  82268. /** Directory where registry files are stored.
  82269. * @var string
  82270. */
  82271. var $statedir = '';
  82272. /** File where the file map is stored
  82273. * @var string
  82274. */
  82275. var $filemap = '';
  82276. /** Directory where registry files for channels are stored.
  82277. * @var string
  82278. */
  82279. var $channelsdir = '';
  82280. /** Name of file used for locking the registry
  82281. * @var string
  82282. */
  82283. var $lockfile = '';
  82284. /** File descriptor used during locking
  82285. * @var resource
  82286. */
  82287. var $lock_fp = null;
  82288. /** Mode used during locking
  82289. * @var int
  82290. */
  82291. var $lock_mode = 0; // XXX UNUSED
  82292. /** Cache of package information. Structure:
  82293. * array(
  82294. * 'package' => array('id' => ... ),
  82295. * ... )
  82296. * @var array
  82297. */
  82298. var $pkginfo_cache = array();
  82299. /** Cache of file map. Structure:
  82300. * array( '/path/to/file' => 'package', ... )
  82301. * @var array
  82302. */
  82303. var $filemap_cache = array();
  82304. /**
  82305. * @var false|PEAR_ChannelFile
  82306. */
  82307. var $_pearChannel;
  82308. /**
  82309. * @var false|PEAR_ChannelFile
  82310. */
  82311. var $_peclChannel;
  82312. /**
  82313. * @var false|PEAR_ChannelFile
  82314. */
  82315. var $_docChannel;
  82316. /**
  82317. * @var PEAR_DependencyDB
  82318. */
  82319. var $_dependencyDB;
  82320. /**
  82321. * @var PEAR_Config
  82322. */
  82323. var $_config;
  82324. /**
  82325. * PEAR_Registry constructor.
  82326. *
  82327. * @param string (optional) PEAR install directory (for .php files)
  82328. * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PEAR channel, if
  82329. * default values are not desired. Only used the very first time a PEAR
  82330. * repository is initialized
  82331. * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PECL channel, if
  82332. * default values are not desired. Only used the very first time a PEAR
  82333. * repository is initialized
  82334. *
  82335. * @access public
  82336. */
  82337. function __construct($pear_install_dir = PEAR_INSTALL_DIR, $pear_channel = false,
  82338. $pecl_channel = false, $pear_metadata_dir = '')
  82339. {
  82340. parent::__construct();
  82341. $this->setInstallDir($pear_install_dir, $pear_metadata_dir);
  82342. $this->_pearChannel = $pear_channel;
  82343. $this->_peclChannel = $pecl_channel;
  82344. $this->_config = false;
  82345. }
  82346. function setInstallDir($pear_install_dir = PEAR_INSTALL_DIR, $pear_metadata_dir = '')
  82347. {
  82348. $ds = DIRECTORY_SEPARATOR;
  82349. $this->install_dir = $pear_install_dir;
  82350. if (!$pear_metadata_dir) {
  82351. $pear_metadata_dir = $pear_install_dir;
  82352. }
  82353. $this->channelsdir = $pear_metadata_dir.$ds.'.channels';
  82354. $this->statedir = $pear_metadata_dir.$ds.'.registry';
  82355. $this->filemap = $pear_metadata_dir.$ds.'.filemap';
  82356. $this->lockfile = $pear_metadata_dir.$ds.'.lock';
  82357. }
  82358. function hasWriteAccess()
  82359. {
  82360. if (!file_exists($this->install_dir)) {
  82361. $dir = $this->install_dir;
  82362. while ($dir && $dir != '.') {
  82363. $olddir = $dir;
  82364. $dir = dirname($dir);
  82365. if ($dir != '.' && file_exists($dir)) {
  82366. if (is_writeable($dir)) {
  82367. return true;
  82368. }
  82369. return false;
  82370. }
  82371. if ($dir == $olddir) { // this can happen in safe mode
  82372. return @is_writable($dir);
  82373. }
  82374. }
  82375. return false;
  82376. }
  82377. return is_writeable($this->install_dir);
  82378. }
  82379. function setConfig(&$config, $resetInstallDir = true)
  82380. {
  82381. $this->_config = &$config;
  82382. if ($resetInstallDir) {
  82383. $this->setInstallDir($config->get('php_dir'), $config->get('metadata_dir'));
  82384. }
  82385. }
  82386. function _initializeChannelDirs()
  82387. {
  82388. static $running = false;
  82389. if (!$running) {
  82390. $running = true;
  82391. $ds = DIRECTORY_SEPARATOR;
  82392. if (!is_dir($this->channelsdir) ||
  82393. !file_exists($this->channelsdir . $ds . 'pear.php.net.reg') ||
  82394. !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') ||
  82395. !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') ||
  82396. !file_exists($this->channelsdir . $ds . '__uri.reg')) {
  82397. if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
  82398. $pear_channel = $this->_pearChannel;
  82399. if (!is_a($pear_channel, 'PEAR_ChannelFile') || !$pear_channel->validate()) {
  82400. if (!class_exists('PEAR_ChannelFile')) {
  82401. require_once 'phar://go-pear.phar/' . 'PEAR/ChannelFile.php';
  82402. }
  82403. $pear_channel = new PEAR_ChannelFile;
  82404. $pear_channel->setAlias('pear');
  82405. $pear_channel->setServer('pear.php.net');
  82406. $pear_channel->setSummary('PHP Extension and Application Repository');
  82407. $pear_channel->setDefaultPEARProtocols();
  82408. $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/');
  82409. $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/');
  82410. $pear_channel->setBaseURL('REST1.3', 'http://pear.php.net/rest/');
  82411. //$pear_channel->setBaseURL('REST1.4', 'http://pear.php.net/rest/');
  82412. } else {
  82413. $pear_channel->setServer('pear.php.net');
  82414. $pear_channel->setAlias('pear');
  82415. }
  82416. $pear_channel->validate();
  82417. $this->_addChannel($pear_channel);
  82418. }
  82419. if (!file_exists($this->channelsdir . $ds . 'pecl.php.net.reg')) {
  82420. $pecl_channel = $this->_peclChannel;
  82421. if (!is_a($pecl_channel, 'PEAR_ChannelFile') || !$pecl_channel->validate()) {
  82422. if (!class_exists('PEAR_ChannelFile')) {
  82423. require_once 'phar://go-pear.phar/' . 'PEAR/ChannelFile.php';
  82424. }
  82425. $pecl_channel = new PEAR_ChannelFile;
  82426. $pecl_channel->setAlias('pecl');
  82427. $pecl_channel->setServer('pecl.php.net');
  82428. $pecl_channel->setSummary('PHP Extension Community Library');
  82429. $pecl_channel->setDefaultPEARProtocols();
  82430. $pecl_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/');
  82431. $pecl_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/');
  82432. $pecl_channel->setValidationPackage('PEAR_Validator_PECL', '1.0');
  82433. } else {
  82434. $pecl_channel->setServer('pecl.php.net');
  82435. $pecl_channel->setAlias('pecl');
  82436. }
  82437. $pecl_channel->validate();
  82438. $this->_addChannel($pecl_channel);
  82439. }
  82440. if (!file_exists($this->channelsdir . $ds . 'doc.php.net.reg')) {
  82441. $doc_channel = $this->_docChannel;
  82442. if (!is_a($doc_channel, 'PEAR_ChannelFile') || !$doc_channel->validate()) {
  82443. if (!class_exists('PEAR_ChannelFile')) {
  82444. require_once 'phar://go-pear.phar/' . 'PEAR/ChannelFile.php';
  82445. }
  82446. $doc_channel = new PEAR_ChannelFile;
  82447. $doc_channel->setAlias('phpdocs');
  82448. $doc_channel->setServer('doc.php.net');
  82449. $doc_channel->setSummary('PHP Documentation Team');
  82450. $doc_channel->setDefaultPEARProtocols();
  82451. $doc_channel->setBaseURL('REST1.0', 'http://doc.php.net/rest/');
  82452. $doc_channel->setBaseURL('REST1.1', 'http://doc.php.net/rest/');
  82453. $doc_channel->setBaseURL('REST1.3', 'http://doc.php.net/rest/');
  82454. } else {
  82455. $doc_channel->setServer('doc.php.net');
  82456. $doc_channel->setAlias('doc');
  82457. }
  82458. $doc_channel->validate();
  82459. $this->_addChannel($doc_channel);
  82460. }
  82461. if (!file_exists($this->channelsdir . $ds . '__uri.reg')) {
  82462. if (!class_exists('PEAR_ChannelFile')) {
  82463. require_once 'phar://go-pear.phar/' . 'PEAR/ChannelFile.php';
  82464. }
  82465. $private = new PEAR_ChannelFile;
  82466. $private->setName('__uri');
  82467. $private->setDefaultPEARProtocols();
  82468. $private->setBaseURL('REST1.0', '****');
  82469. $private->setSummary('Pseudo-channel for static packages');
  82470. $this->_addChannel($private);
  82471. }
  82472. $this->_rebuildFileMap();
  82473. }
  82474. $running = false;
  82475. }
  82476. }
  82477. function _initializeDirs()
  82478. {
  82479. $ds = DIRECTORY_SEPARATOR;
  82480. // XXX Compatibility code should be removed in the future
  82481. // rename all registry files if any to lowercase
  82482. if (!OS_WINDOWS && file_exists($this->statedir) && is_dir($this->statedir) &&
  82483. $handle = opendir($this->statedir)) {
  82484. $dest = $this->statedir . $ds;
  82485. while (false !== ($file = readdir($handle))) {
  82486. if (preg_match('/^.*[A-Z].*\.reg\\z/', $file)) {
  82487. rename($dest . $file, $dest . strtolower($file));
  82488. }
  82489. }
  82490. closedir($handle);
  82491. }
  82492. $this->_initializeChannelDirs();
  82493. if (!file_exists($this->filemap)) {
  82494. $this->_rebuildFileMap();
  82495. }
  82496. $this->_initializeDepDB();
  82497. }
  82498. function _initializeDepDB()
  82499. {
  82500. if (!isset($this->_dependencyDB)) {
  82501. static $initializing = false;
  82502. if (!$initializing) {
  82503. $initializing = true;
  82504. if (!$this->_config) { // never used?
  82505. $file = OS_WINDOWS ? 'pear.ini' : '.pearrc';
  82506. $this->_config = new PEAR_Config($this->statedir . DIRECTORY_SEPARATOR .
  82507. $file);
  82508. $this->_config->setRegistry($this);
  82509. $this->_config->set('php_dir', $this->install_dir);
  82510. }
  82511. $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config);
  82512. if (PEAR::isError($this->_dependencyDB)) {
  82513. // attempt to recover by removing the dep db
  82514. if (file_exists($this->_config->get('metadata_dir', null, 'pear.php.net') .
  82515. DIRECTORY_SEPARATOR . '.depdb')) {
  82516. @unlink($this->_config->get('metadata_dir', null, 'pear.php.net') .
  82517. DIRECTORY_SEPARATOR . '.depdb');
  82518. }
  82519. $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config);
  82520. if (PEAR::isError($this->_dependencyDB)) {
  82521. echo $this->_dependencyDB->getMessage();
  82522. echo 'Unrecoverable error';
  82523. exit(1);
  82524. }
  82525. }
  82526. $initializing = false;
  82527. }
  82528. }
  82529. }
  82530. /**
  82531. * PEAR_Registry destructor. Makes sure no locks are forgotten.
  82532. *
  82533. * @access private
  82534. */
  82535. function _PEAR_Registry()
  82536. {
  82537. parent::_PEAR();
  82538. if (is_resource($this->lock_fp)) {
  82539. $this->_unlock();
  82540. }
  82541. }
  82542. /**
  82543. * Make sure the directory where we keep registry files exists.
  82544. *
  82545. * @return bool TRUE if directory exists, FALSE if it could not be
  82546. * created
  82547. *
  82548. * @access private
  82549. */
  82550. function _assertStateDir($channel = false)
  82551. {
  82552. if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
  82553. return $this->_assertChannelStateDir($channel);
  82554. }
  82555. static $init = false;
  82556. if (!file_exists($this->statedir)) {
  82557. if (!$this->hasWriteAccess()) {
  82558. return false;
  82559. }
  82560. require_once 'phar://go-pear.phar/' . 'System.php';
  82561. if (!System::mkdir(array('-p', $this->statedir))) {
  82562. return $this->raiseError("could not create directory '{$this->statedir}'");
  82563. }
  82564. $init = true;
  82565. } elseif (!is_dir($this->statedir)) {
  82566. return $this->raiseError('Cannot create directory ' . $this->statedir . ', ' .
  82567. 'it already exists and is not a directory');
  82568. }
  82569. $ds = DIRECTORY_SEPARATOR;
  82570. if (!file_exists($this->channelsdir)) {
  82571. if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg') ||
  82572. !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') ||
  82573. !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') ||
  82574. !file_exists($this->channelsdir . $ds . '__uri.reg')) {
  82575. $init = true;
  82576. }
  82577. } elseif (!is_dir($this->channelsdir)) {
  82578. return $this->raiseError('Cannot create directory ' . $this->channelsdir . ', ' .
  82579. 'it already exists and is not a directory');
  82580. }
  82581. if ($init) {
  82582. static $running = false;
  82583. if (!$running) {
  82584. $running = true;
  82585. $this->_initializeDirs();
  82586. $running = false;
  82587. $init = false;
  82588. }
  82589. } else {
  82590. $this->_initializeDepDB();
  82591. }
  82592. return true;
  82593. }
  82594. /**
  82595. * Make sure the directory where we keep registry files exists for a non-standard channel.
  82596. *
  82597. * @param string channel name
  82598. * @return bool TRUE if directory exists, FALSE if it could not be
  82599. * created
  82600. *
  82601. * @access private
  82602. */
  82603. function _assertChannelStateDir($channel)
  82604. {
  82605. $ds = DIRECTORY_SEPARATOR;
  82606. if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
  82607. if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
  82608. $this->_initializeChannelDirs();
  82609. }
  82610. return $this->_assertStateDir($channel);
  82611. }
  82612. $channelDir = $this->_channelDirectoryName($channel);
  82613. if (!is_dir($this->channelsdir) ||
  82614. !file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
  82615. $this->_initializeChannelDirs();
  82616. }
  82617. if (!file_exists($channelDir)) {
  82618. if (!$this->hasWriteAccess()) {
  82619. return false;
  82620. }
  82621. require_once 'phar://go-pear.phar/' . 'System.php';
  82622. if (!System::mkdir(array('-p', $channelDir))) {
  82623. return $this->raiseError("could not create directory '" . $channelDir .
  82624. "'");
  82625. }
  82626. } elseif (!is_dir($channelDir)) {
  82627. return $this->raiseError("could not create directory '" . $channelDir .
  82628. "', already exists and is not a directory");
  82629. }
  82630. return true;
  82631. }
  82632. /**
  82633. * Make sure the directory where we keep registry files for channels exists
  82634. *
  82635. * @return bool TRUE if directory exists, FALSE if it could not be
  82636. * created
  82637. *
  82638. * @access private
  82639. */
  82640. function _assertChannelDir()
  82641. {
  82642. if (!file_exists($this->channelsdir)) {
  82643. if (!$this->hasWriteAccess()) {
  82644. return false;
  82645. }
  82646. require_once 'phar://go-pear.phar/' . 'System.php';
  82647. if (!System::mkdir(array('-p', $this->channelsdir))) {
  82648. return $this->raiseError("could not create directory '{$this->channelsdir}'");
  82649. }
  82650. } elseif (!is_dir($this->channelsdir)) {
  82651. return $this->raiseError("could not create directory '{$this->channelsdir}" .
  82652. "', it already exists and is not a directory");
  82653. }
  82654. if (!file_exists($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) {
  82655. if (!$this->hasWriteAccess()) {
  82656. return false;
  82657. }
  82658. require_once 'phar://go-pear.phar/' . 'System.php';
  82659. if (!System::mkdir(array('-p', $this->channelsdir . DIRECTORY_SEPARATOR . '.alias'))) {
  82660. return $this->raiseError("could not create directory '{$this->channelsdir}/.alias'");
  82661. }
  82662. } elseif (!is_dir($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) {
  82663. return $this->raiseError("could not create directory '{$this->channelsdir}" .
  82664. "/.alias', it already exists and is not a directory");
  82665. }
  82666. return true;
  82667. }
  82668. /**
  82669. * Get the name of the file where data for a given package is stored.
  82670. *
  82671. * @param string channel name, or false if this is a PEAR package
  82672. * @param string package name
  82673. *
  82674. * @return string registry file name
  82675. *
  82676. * @access public
  82677. */
  82678. function _packageFileName($package, $channel = false)
  82679. {
  82680. if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
  82681. return $this->_channelDirectoryName($channel) . DIRECTORY_SEPARATOR .
  82682. strtolower($package) . '.reg';
  82683. }
  82684. return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg';
  82685. }
  82686. /**
  82687. * Get the name of the file where data for a given channel is stored.
  82688. * @param string channel name
  82689. * @return string registry file name
  82690. */
  82691. function _channelFileName($channel, $noaliases = false)
  82692. {
  82693. if (!$noaliases) {
  82694. if (file_exists($this->_getChannelAliasFileName($channel))) {
  82695. $channel = implode('', file($this->_getChannelAliasFileName($channel)));
  82696. }
  82697. }
  82698. return $this->channelsdir . DIRECTORY_SEPARATOR . str_replace('/', '_',
  82699. strtolower($channel)) . '.reg';
  82700. }
  82701. /**
  82702. * @param string
  82703. * @return string
  82704. */
  82705. function _getChannelAliasFileName($alias)
  82706. {
  82707. return $this->channelsdir . DIRECTORY_SEPARATOR . '.alias' .
  82708. DIRECTORY_SEPARATOR . str_replace('/', '_', strtolower($alias)) . '.txt';
  82709. }
  82710. /**
  82711. * Get the name of a channel from its alias
  82712. */
  82713. function _getChannelFromAlias($channel)
  82714. {
  82715. if (!$this->_channelExists($channel)) {
  82716. if ($channel == 'pear.php.net') {
  82717. return 'pear.php.net';
  82718. }
  82719. if ($channel == 'pecl.php.net') {
  82720. return 'pecl.php.net';
  82721. }
  82722. if ($channel == 'doc.php.net') {
  82723. return 'doc.php.net';
  82724. }
  82725. if ($channel == '__uri') {
  82726. return '__uri';
  82727. }
  82728. return false;
  82729. }
  82730. $channel = strtolower($channel);
  82731. if (file_exists($this->_getChannelAliasFileName($channel))) {
  82732. // translate an alias to an actual channel
  82733. return implode('', file($this->_getChannelAliasFileName($channel)));
  82734. }
  82735. return $channel;
  82736. }
  82737. /**
  82738. * Get the alias of a channel from its alias or its name
  82739. */
  82740. function _getAlias($channel)
  82741. {
  82742. if (!$this->_channelExists($channel)) {
  82743. if ($channel == 'pear.php.net') {
  82744. return 'pear';
  82745. }
  82746. if ($channel == 'pecl.php.net') {
  82747. return 'pecl';
  82748. }
  82749. if ($channel == 'doc.php.net') {
  82750. return 'phpdocs';
  82751. }
  82752. return false;
  82753. }
  82754. $channel = $this->_getChannel($channel);
  82755. if (PEAR::isError($channel)) {
  82756. return $channel;
  82757. }
  82758. return $channel->getAlias();
  82759. }
  82760. /**
  82761. * Get the name of the file where data for a given package is stored.
  82762. *
  82763. * @param string channel name, or false if this is a PEAR package
  82764. * @param string package name
  82765. *
  82766. * @return string registry file name
  82767. *
  82768. * @access public
  82769. */
  82770. function _channelDirectoryName($channel)
  82771. {
  82772. if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
  82773. return $this->statedir;
  82774. }
  82775. $ch = $this->_getChannelFromAlias($channel);
  82776. if (!$ch) {
  82777. $ch = $channel;
  82778. }
  82779. return $this->statedir . DIRECTORY_SEPARATOR . strtolower('.channel.' .
  82780. str_replace('/', '_', $ch));
  82781. }
  82782. function _openPackageFile($package, $mode, $channel = false)
  82783. {
  82784. if (!$this->_assertStateDir($channel)) {
  82785. return null;
  82786. }
  82787. if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) {
  82788. return null;
  82789. }
  82790. $file = $this->_packageFileName($package, $channel);
  82791. if (!file_exists($file) && $mode == 'r' || $mode == 'rb') {
  82792. return null;
  82793. }
  82794. $fp = @fopen($file, $mode);
  82795. if (!$fp) {
  82796. return null;
  82797. }
  82798. return $fp;
  82799. }
  82800. function _closePackageFile($fp)
  82801. {
  82802. fclose($fp);
  82803. }
  82804. function _openChannelFile($channel, $mode)
  82805. {
  82806. if (!$this->_assertChannelDir()) {
  82807. return null;
  82808. }
  82809. if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) {
  82810. return null;
  82811. }
  82812. $file = $this->_channelFileName($channel);
  82813. if (!file_exists($file) && $mode == 'r' || $mode == 'rb') {
  82814. return null;
  82815. }
  82816. $fp = @fopen($file, $mode);
  82817. if (!$fp) {
  82818. return null;
  82819. }
  82820. return $fp;
  82821. }
  82822. function _closeChannelFile($fp)
  82823. {
  82824. fclose($fp);
  82825. }
  82826. function _rebuildFileMap()
  82827. {
  82828. if (!class_exists('PEAR_Installer_Role')) {
  82829. require_once 'phar://go-pear.phar/' . 'PEAR/Installer/Role.php';
  82830. }
  82831. $channels = $this->_listAllPackages();
  82832. $files = array();
  82833. foreach ($channels as $channel => $packages) {
  82834. foreach ($packages as $package) {
  82835. $version = $this->_packageInfo($package, 'version', $channel);
  82836. $filelist = $this->_packageInfo($package, 'filelist', $channel);
  82837. if (!is_array($filelist)) {
  82838. continue;
  82839. }
  82840. foreach ($filelist as $name => $attrs) {
  82841. if (isset($attrs['attribs'])) {
  82842. $attrs = $attrs['attribs'];
  82843. }
  82844. // it is possible for conflicting packages in different channels to
  82845. // conflict with data files/doc files
  82846. if ($name == 'dirtree') {
  82847. continue;
  82848. }
  82849. if (isset($attrs['role']) && !in_array($attrs['role'],
  82850. PEAR_Installer_Role::getInstallableRoles())) {
  82851. // these are not installed
  82852. continue;
  82853. }
  82854. if (isset($attrs['role']) && !in_array($attrs['role'],
  82855. PEAR_Installer_Role::getBaseinstallRoles())) {
  82856. $attrs['baseinstalldir'] = $package;
  82857. }
  82858. if (isset($attrs['baseinstalldir'])) {
  82859. $file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name;
  82860. } else {
  82861. $file = $name;
  82862. }
  82863. $file = preg_replace(',^/+,', '', $file);
  82864. if ($channel != 'pear.php.net') {
  82865. if (!isset($files[$attrs['role']])) {
  82866. $files[$attrs['role']] = array();
  82867. }
  82868. $files[$attrs['role']][$file] = array(strtolower($channel),
  82869. strtolower($package));
  82870. } else {
  82871. if (!isset($files[$attrs['role']])) {
  82872. $files[$attrs['role']] = array();
  82873. }
  82874. $files[$attrs['role']][$file] = strtolower($package);
  82875. }
  82876. }
  82877. }
  82878. }
  82879. $this->_assertStateDir();
  82880. if (!$this->hasWriteAccess()) {
  82881. return false;
  82882. }
  82883. $fp = @fopen($this->filemap, 'wb');
  82884. if (!$fp) {
  82885. return false;
  82886. }
  82887. $this->filemap_cache = $files;
  82888. fwrite($fp, serialize($files));
  82889. fclose($fp);
  82890. return true;
  82891. }
  82892. function _readFileMap()
  82893. {
  82894. if (!file_exists($this->filemap)) {
  82895. return array();
  82896. }
  82897. $fp = @fopen($this->filemap, 'r');
  82898. if (!$fp) {
  82899. $last_errormsg = '';
  82900. $last_error = error_get_last();
  82901. if (!empty($last_error['message'])) {
  82902. $last_errormsg = $last_error['message'];
  82903. }
  82904. return $this->raiseError('PEAR_Registry: could not open filemap "' . $this->filemap . '"', PEAR_REGISTRY_ERROR_FILE, null, null, $last_errormsg);
  82905. }
  82906. clearstatcache();
  82907. $fsize = filesize($this->filemap);
  82908. fclose($fp);
  82909. $data = file_get_contents($this->filemap);
  82910. $tmp = unserialize($data);
  82911. if (!$tmp && $fsize > 7) {
  82912. return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data);
  82913. }
  82914. $this->filemap_cache = $tmp;
  82915. return true;
  82916. }
  82917. /**
  82918. * Lock the registry.
  82919. *
  82920. * @param integer lock mode, one of LOCK_EX, LOCK_SH or LOCK_UN.
  82921. * See flock manual for more information.
  82922. *
  82923. * @return bool TRUE on success, FALSE if locking failed, or a
  82924. * PEAR error if some other error occurs (such as the
  82925. * lock file not being writable).
  82926. *
  82927. * @access private
  82928. */
  82929. function _lock($mode = LOCK_EX)
  82930. {
  82931. if (stristr(php_uname(), 'Windows 9')) {
  82932. return true;
  82933. }
  82934. if ($mode != LOCK_UN && is_resource($this->lock_fp)) {
  82935. // XXX does not check type of lock (LOCK_SH/LOCK_EX)
  82936. return true;
  82937. }
  82938. if (!$this->_assertStateDir()) {
  82939. if ($mode == LOCK_EX) {
  82940. return $this->raiseError('Registry directory is not writeable by the current user');
  82941. }
  82942. return true;
  82943. }
  82944. $open_mode = 'w';
  82945. // XXX People reported problems with LOCK_SH and 'w'
  82946. if ($mode === LOCK_SH || $mode === LOCK_UN) {
  82947. if (!file_exists($this->lockfile)) {
  82948. touch($this->lockfile);
  82949. }
  82950. $open_mode = 'r';
  82951. }
  82952. if (!is_resource($this->lock_fp)) {
  82953. $this->lock_fp = @fopen($this->lockfile, $open_mode);
  82954. }
  82955. if (!is_resource($this->lock_fp)) {
  82956. $this->lock_fp = null;
  82957. return $this->raiseError("could not create lock file" .
  82958. (isset($php_errormsg) ? ": " . $php_errormsg : ""));
  82959. }
  82960. if (!(int)flock($this->lock_fp, $mode)) {
  82961. switch ($mode) {
  82962. case LOCK_SH: $str = 'shared'; break;
  82963. case LOCK_EX: $str = 'exclusive'; break;
  82964. case LOCK_UN: $str = 'unlock'; break;
  82965. default: $str = 'unknown'; break;
  82966. }
  82967. //is resource at this point, close it on error.
  82968. fclose($this->lock_fp);
  82969. $this->lock_fp = null;
  82970. return $this->raiseError("could not acquire $str lock ($this->lockfile)",
  82971. PEAR_REGISTRY_ERROR_LOCK);
  82972. }
  82973. return true;
  82974. }
  82975. function _unlock()
  82976. {
  82977. $ret = $this->_lock(LOCK_UN);
  82978. if (is_resource($this->lock_fp)) {
  82979. fclose($this->lock_fp);
  82980. }
  82981. $this->lock_fp = null;
  82982. return $ret;
  82983. }
  82984. function _packageExists($package, $channel = false)
  82985. {
  82986. return file_exists($this->_packageFileName($package, $channel));
  82987. }
  82988. /**
  82989. * Determine whether a channel exists in the registry
  82990. *
  82991. * @param string Channel name
  82992. * @param bool if true, then aliases will be ignored
  82993. * @return boolean
  82994. */
  82995. function _channelExists($channel, $noaliases = false)
  82996. {
  82997. $a = file_exists($this->_channelFileName($channel, $noaliases));
  82998. if (!$a && $channel == 'pear.php.net') {
  82999. return true;
  83000. }
  83001. if (!$a && $channel == 'pecl.php.net') {
  83002. return true;
  83003. }
  83004. if (!$a && $channel == 'doc.php.net') {
  83005. return true;
  83006. }
  83007. return $a;
  83008. }
  83009. /**
  83010. * Determine whether a mirror exists within the default channel in the registry
  83011. *
  83012. * @param string Channel name
  83013. * @param string Mirror name
  83014. *
  83015. * @return boolean
  83016. */
  83017. function _mirrorExists($channel, $mirror)
  83018. {
  83019. $data = $this->_channelInfo($channel);
  83020. if (!isset($data['servers']['mirror'])) {
  83021. return false;
  83022. }
  83023. foreach ($data['servers']['mirror'] as $m) {
  83024. if ($m['attribs']['host'] == $mirror) {
  83025. return true;
  83026. }
  83027. }
  83028. return false;
  83029. }
  83030. /**
  83031. * @param PEAR_ChannelFile Channel object
  83032. * @param donotuse
  83033. * @param string Last-Modified HTTP tag from remote request
  83034. * @return boolean|PEAR_Error True on creation, false if it already exists
  83035. */
  83036. function _addChannel($channel, $update = false, $lastmodified = false)
  83037. {
  83038. if (!is_a($channel, 'PEAR_ChannelFile')) {
  83039. return false;
  83040. }
  83041. if (!$channel->validate()) {
  83042. return false;
  83043. }
  83044. if (file_exists($this->_channelFileName($channel->getName()))) {
  83045. if (!$update) {
  83046. return false;
  83047. }
  83048. $checker = $this->_getChannel($channel->getName());
  83049. if (PEAR::isError($checker)) {
  83050. return $checker;
  83051. }
  83052. if ($channel->getAlias() != $checker->getAlias()) {
  83053. if (file_exists($this->_getChannelAliasFileName($checker->getAlias()))) {
  83054. @unlink($this->_getChannelAliasFileName($checker->getAlias()));
  83055. }
  83056. }
  83057. } else {
  83058. if ($update && !in_array($channel->getName(), array('pear.php.net', 'pecl.php.net', 'doc.php.net'))) {
  83059. return false;
  83060. }
  83061. }
  83062. $ret = $this->_assertChannelDir();
  83063. if (PEAR::isError($ret)) {
  83064. return $ret;
  83065. }
  83066. $ret = $this->_assertChannelStateDir($channel->getName());
  83067. if (PEAR::isError($ret)) {
  83068. return $ret;
  83069. }
  83070. if ($channel->getAlias() != $channel->getName()) {
  83071. if (file_exists($this->_getChannelAliasFileName($channel->getAlias())) &&
  83072. $this->_getChannelFromAlias($channel->getAlias()) != $channel->getName()) {
  83073. $channel->setAlias($channel->getName());
  83074. }
  83075. if (!$this->hasWriteAccess()) {
  83076. return false;
  83077. }
  83078. $fp = @fopen($this->_getChannelAliasFileName($channel->getAlias()), 'w');
  83079. if (!$fp) {
  83080. return false;
  83081. }
  83082. fwrite($fp, $channel->getName());
  83083. fclose($fp);
  83084. }
  83085. if (!$this->hasWriteAccess()) {
  83086. return false;
  83087. }
  83088. $fp = @fopen($this->_channelFileName($channel->getName()), 'wb');
  83089. if (!$fp) {
  83090. return false;
  83091. }
  83092. $info = $channel->toArray();
  83093. if ($lastmodified) {
  83094. $info['_lastmodified'] = $lastmodified;
  83095. } else {
  83096. $info['_lastmodified'] = self::getSourceDateEpoch();
  83097. }
  83098. fwrite($fp, serialize($info));
  83099. fclose($fp);
  83100. return true;
  83101. }
  83102. /**
  83103. * Deletion fails if there are any packages installed from the channel
  83104. * @param string|PEAR_ChannelFile channel name
  83105. * @return boolean|PEAR_Error True on deletion, false if it doesn't exist
  83106. */
  83107. function _deleteChannel($channel)
  83108. {
  83109. if (!is_string($channel)) {
  83110. if (!is_a($channel, 'PEAR_ChannelFile')) {
  83111. return false;
  83112. }
  83113. if (!$channel->validate()) {
  83114. return false;
  83115. }
  83116. $channel = $channel->getName();
  83117. }
  83118. if ($this->_getChannelFromAlias($channel) == '__uri') {
  83119. return false;
  83120. }
  83121. if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') {
  83122. return false;
  83123. }
  83124. if ($this->_getChannelFromAlias($channel) == 'doc.php.net') {
  83125. return false;
  83126. }
  83127. if (!$this->_channelExists($channel)) {
  83128. return false;
  83129. }
  83130. if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
  83131. return false;
  83132. }
  83133. $channel = $this->_getChannelFromAlias($channel);
  83134. if ($channel == 'pear.php.net') {
  83135. return false;
  83136. }
  83137. $test = $this->_listChannelPackages($channel);
  83138. if (count($test)) {
  83139. return false;
  83140. }
  83141. $test = @rmdir($this->_channelDirectoryName($channel));
  83142. if (!$test) {
  83143. return false;
  83144. }
  83145. $file = $this->_getChannelAliasFileName($this->_getAlias($channel));
  83146. if (file_exists($file)) {
  83147. $test = @unlink($file);
  83148. if (!$test) {
  83149. return false;
  83150. }
  83151. }
  83152. $file = $this->_channelFileName($channel);
  83153. $ret = true;
  83154. if (file_exists($file)) {
  83155. $ret = @unlink($file);
  83156. }
  83157. return $ret;
  83158. }
  83159. /**
  83160. * Determine whether a channel exists in the registry
  83161. * @param string Channel Alias
  83162. * @return boolean
  83163. */
  83164. function _isChannelAlias($alias)
  83165. {
  83166. return file_exists($this->_getChannelAliasFileName($alias));
  83167. }
  83168. /**
  83169. * @param string|null
  83170. * @param string|null
  83171. * @param string|null
  83172. * @return array|null
  83173. * @access private
  83174. */
  83175. function _packageInfo($package = null, $key = null, $channel = 'pear.php.net')
  83176. {
  83177. if ($package === null) {
  83178. if ($channel === null) {
  83179. $channels = $this->_listChannels();
  83180. $ret = array();
  83181. foreach ($channels as $channel) {
  83182. $channel = strtolower($channel);
  83183. $ret[$channel] = array();
  83184. $packages = $this->_listPackages($channel);
  83185. foreach ($packages as $package) {
  83186. $ret[$channel][] = $this->_packageInfo($package, null, $channel);
  83187. }
  83188. }
  83189. return $ret;
  83190. }
  83191. $ps = $this->_listPackages($channel);
  83192. if (!count($ps)) {
  83193. return array();
  83194. }
  83195. return array_map(array(&$this, '_packageInfo'),
  83196. $ps, array_fill(0, count($ps), null),
  83197. array_fill(0, count($ps), $channel));
  83198. }
  83199. $fp = $this->_openPackageFile($package, 'r', $channel);
  83200. if ($fp === null) {
  83201. return null;
  83202. }
  83203. clearstatcache();
  83204. $this->_closePackageFile($fp);
  83205. $data = file_get_contents($this->_packageFileName($package, $channel));
  83206. $data = @unserialize($data);
  83207. if ($key === null) {
  83208. return $data;
  83209. }
  83210. // compatibility for package.xml version 2.0
  83211. if (isset($data['old'][$key])) {
  83212. return $data['old'][$key];
  83213. }
  83214. if (isset($data[$key])) {
  83215. return $data[$key];
  83216. }
  83217. return null;
  83218. }
  83219. /**
  83220. * @param string Channel name
  83221. * @param bool whether to strictly retrieve info of channels, not just aliases
  83222. * @return array|null
  83223. */
  83224. function _channelInfo($channel, $noaliases = false)
  83225. {
  83226. if (!$this->_channelExists($channel, $noaliases)) {
  83227. return null;
  83228. }
  83229. $fp = $this->_openChannelFile($channel, 'r');
  83230. if ($fp === null) {
  83231. return null;
  83232. }
  83233. clearstatcache();
  83234. $this->_closeChannelFile($fp);
  83235. $data = file_get_contents($this->_channelFileName($channel));
  83236. $data = unserialize($data);
  83237. return $data;
  83238. }
  83239. function _listChannels()
  83240. {
  83241. $channellist = array();
  83242. if (!file_exists($this->channelsdir) || !is_dir($this->channelsdir)) {
  83243. return array('pear.php.net', 'pecl.php.net', 'doc.php.net', '__uri');
  83244. }
  83245. $dp = opendir($this->channelsdir);
  83246. while ($ent = readdir($dp)) {
  83247. if ($ent[0] == '.' || substr($ent, -4) != '.reg') {
  83248. continue;
  83249. }
  83250. if ($ent == '__uri.reg') {
  83251. $channellist[] = '__uri';
  83252. continue;
  83253. }
  83254. $channellist[] = str_replace('_', '/', substr($ent, 0, -4));
  83255. }
  83256. closedir($dp);
  83257. if (!in_array('pear.php.net', $channellist)) {
  83258. $channellist[] = 'pear.php.net';
  83259. }
  83260. if (!in_array('pecl.php.net', $channellist)) {
  83261. $channellist[] = 'pecl.php.net';
  83262. }
  83263. if (!in_array('doc.php.net', $channellist)) {
  83264. $channellist[] = 'doc.php.net';
  83265. }
  83266. if (!in_array('__uri', $channellist)) {
  83267. $channellist[] = '__uri';
  83268. }
  83269. natsort($channellist);
  83270. return $channellist;
  83271. }
  83272. function _listPackages($channel = false)
  83273. {
  83274. if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
  83275. return $this->_listChannelPackages($channel);
  83276. }
  83277. if (!file_exists($this->statedir) || !is_dir($this->statedir)) {
  83278. return array();
  83279. }
  83280. $pkglist = array();
  83281. $dp = opendir($this->statedir);
  83282. if (!$dp) {
  83283. return $pkglist;
  83284. }
  83285. while ($ent = readdir($dp)) {
  83286. if ($ent[0] == '.' || substr($ent, -4) != '.reg') {
  83287. continue;
  83288. }
  83289. $pkglist[] = substr($ent, 0, -4);
  83290. }
  83291. closedir($dp);
  83292. sort($pkglist);
  83293. return $pkglist;
  83294. }
  83295. function _listChannelPackages($channel)
  83296. {
  83297. $pkglist = array();
  83298. if (!file_exists($this->_channelDirectoryName($channel)) ||
  83299. !is_dir($this->_channelDirectoryName($channel))) {
  83300. return array();
  83301. }
  83302. $dp = opendir($this->_channelDirectoryName($channel));
  83303. if (!$dp) {
  83304. return $pkglist;
  83305. }
  83306. while ($ent = readdir($dp)) {
  83307. if ($ent[0] == '.' || substr($ent, -4) != '.reg') {
  83308. continue;
  83309. }
  83310. $pkglist[] = substr($ent, 0, -4);
  83311. }
  83312. closedir($dp);
  83313. return $pkglist;
  83314. }
  83315. function _listAllPackages()
  83316. {
  83317. $ret = array();
  83318. foreach ($this->_listChannels() as $channel) {
  83319. $ret[$channel] = $this->_listPackages($channel);
  83320. }
  83321. return $ret;
  83322. }
  83323. /**
  83324. * Add an installed package to the registry
  83325. * @param string package name
  83326. * @param array package info (parsed by PEAR_Common::infoFrom*() methods)
  83327. * @return bool success of saving
  83328. * @access private
  83329. */
  83330. function _addPackage($package, $info)
  83331. {
  83332. if ($this->_packageExists($package)) {
  83333. return false;
  83334. }
  83335. $fp = $this->_openPackageFile($package, 'wb');
  83336. if ($fp === null) {
  83337. return false;
  83338. }
  83339. $info['_lastmodified'] = self::getSourceDateEpoch();
  83340. fwrite($fp, serialize($info));
  83341. $this->_closePackageFile($fp);
  83342. if (isset($info['filelist'])) {
  83343. $this->_rebuildFileMap();
  83344. }
  83345. return true;
  83346. }
  83347. /**
  83348. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  83349. * @return bool
  83350. * @access private
  83351. */
  83352. function _addPackage2($info)
  83353. {
  83354. if (!is_a($info, 'PEAR_PackageFile_v1') && !is_a($info, 'PEAR_PackageFile_v2')) {
  83355. return false;
  83356. }
  83357. if (!$info->validate()) {
  83358. if (class_exists('PEAR_Common')) {
  83359. $ui = PEAR_Frontend::singleton();
  83360. if ($ui) {
  83361. foreach ($info->getValidationWarnings() as $err) {
  83362. $ui->log($err['message'], true);
  83363. }
  83364. }
  83365. }
  83366. return false;
  83367. }
  83368. $channel = $info->getChannel();
  83369. $package = $info->getPackage();
  83370. $save = $info;
  83371. if ($this->_packageExists($package, $channel)) {
  83372. return false;
  83373. }
  83374. if (!$this->_channelExists($channel, true)) {
  83375. return false;
  83376. }
  83377. $info = $info->toArray(true);
  83378. if (!$info) {
  83379. return false;
  83380. }
  83381. $fp = $this->_openPackageFile($package, 'wb', $channel);
  83382. if ($fp === null) {
  83383. return false;
  83384. }
  83385. $info['_lastmodified'] = self::getSourceDateEpoch();
  83386. fwrite($fp, serialize($info));
  83387. $this->_closePackageFile($fp);
  83388. $this->_rebuildFileMap();
  83389. return true;
  83390. }
  83391. /**
  83392. * @param string Package name
  83393. * @param array parsed package.xml 1.0
  83394. * @param bool this parameter is only here for BC. Don't use it.
  83395. * @access private
  83396. */
  83397. function _updatePackage($package, $info, $merge = true)
  83398. {
  83399. $oldinfo = $this->_packageInfo($package);
  83400. if (empty($oldinfo)) {
  83401. return false;
  83402. }
  83403. $fp = $this->_openPackageFile($package, 'w');
  83404. if ($fp === null) {
  83405. return false;
  83406. }
  83407. if (is_object($info)) {
  83408. $info = $info->toArray();
  83409. }
  83410. $info['_lastmodified'] = self::getSourceDateEpoch();
  83411. $newinfo = $info;
  83412. if ($merge) {
  83413. $info = array_merge($oldinfo, $info);
  83414. } else {
  83415. $diff = $info;
  83416. }
  83417. fwrite($fp, serialize($info));
  83418. $this->_closePackageFile($fp);
  83419. if (isset($newinfo['filelist'])) {
  83420. $this->_rebuildFileMap();
  83421. }
  83422. return true;
  83423. }
  83424. /**
  83425. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  83426. * @return bool
  83427. * @access private
  83428. */
  83429. function _updatePackage2($info)
  83430. {
  83431. if (!$this->_packageExists($info->getPackage(), $info->getChannel())) {
  83432. return false;
  83433. }
  83434. $fp = $this->_openPackageFile($info->getPackage(), 'w', $info->getChannel());
  83435. if ($fp === null) {
  83436. return false;
  83437. }
  83438. $save = $info;
  83439. $info = $save->getArray(true);
  83440. $info['_lastmodified'] = self::getSourceDateEpoch();
  83441. fwrite($fp, serialize($info));
  83442. $this->_closePackageFile($fp);
  83443. $this->_rebuildFileMap();
  83444. return true;
  83445. }
  83446. /**
  83447. * @param string Package name
  83448. * @param string Channel name
  83449. * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null
  83450. * @access private
  83451. */
  83452. function &_getPackage($package, $channel = 'pear.php.net')
  83453. {
  83454. $info = $this->_packageInfo($package, null, $channel);
  83455. if ($info === null) {
  83456. return $info;
  83457. }
  83458. $a = $this->_config;
  83459. if (!$a) {
  83460. $this->_config = new PEAR_Config;
  83461. $this->_config->set('php_dir', $this->statedir);
  83462. }
  83463. if (!class_exists('PEAR_PackageFile')) {
  83464. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile.php';
  83465. }
  83466. $pkg = new PEAR_PackageFile($this->_config);
  83467. $pf = &$pkg->fromArray($info);
  83468. return $pf;
  83469. }
  83470. /**
  83471. * @param string channel name
  83472. * @param bool whether to strictly retrieve channel names
  83473. * @return PEAR_ChannelFile|PEAR_Error
  83474. * @access private
  83475. */
  83476. function &_getChannel($channel, $noaliases = false)
  83477. {
  83478. $ch = false;
  83479. if ($this->_channelExists($channel, $noaliases)) {
  83480. $chinfo = $this->_channelInfo($channel, $noaliases);
  83481. if ($chinfo) {
  83482. if (!class_exists('PEAR_ChannelFile')) {
  83483. require_once 'phar://go-pear.phar/' . 'PEAR/ChannelFile.php';
  83484. }
  83485. $ch = &PEAR_ChannelFile::fromArrayWithErrors($chinfo);
  83486. }
  83487. }
  83488. if ($ch) {
  83489. if ($ch->validate()) {
  83490. return $ch;
  83491. }
  83492. foreach ($ch->getErrors(true) as $err) {
  83493. $message = $err['message'] . "\n";
  83494. }
  83495. $ch = PEAR::raiseError($message);
  83496. return $ch;
  83497. }
  83498. if ($this->_getChannelFromAlias($channel) == 'pear.php.net') {
  83499. // the registry is not properly set up, so use defaults
  83500. if (!class_exists('PEAR_ChannelFile')) {
  83501. require_once 'phar://go-pear.phar/' . 'PEAR/ChannelFile.php';
  83502. }
  83503. $pear_channel = new PEAR_ChannelFile;
  83504. $pear_channel->setServer('pear.php.net');
  83505. $pear_channel->setAlias('pear');
  83506. $pear_channel->setSummary('PHP Extension and Application Repository');
  83507. $pear_channel->setDefaultPEARProtocols();
  83508. $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/');
  83509. $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/');
  83510. $pear_channel->setBaseURL('REST1.3', 'http://pear.php.net/rest/');
  83511. return $pear_channel;
  83512. }
  83513. if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') {
  83514. // the registry is not properly set up, so use defaults
  83515. if (!class_exists('PEAR_ChannelFile')) {
  83516. require_once 'phar://go-pear.phar/' . 'PEAR/ChannelFile.php';
  83517. }
  83518. $pear_channel = new PEAR_ChannelFile;
  83519. $pear_channel->setServer('pecl.php.net');
  83520. $pear_channel->setAlias('pecl');
  83521. $pear_channel->setSummary('PHP Extension Community Library');
  83522. $pear_channel->setDefaultPEARProtocols();
  83523. $pear_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/');
  83524. $pear_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/');
  83525. $pear_channel->setValidationPackage('PEAR_Validator_PECL', '1.0');
  83526. return $pear_channel;
  83527. }
  83528. if ($this->_getChannelFromAlias($channel) == 'doc.php.net') {
  83529. // the registry is not properly set up, so use defaults
  83530. if (!class_exists('PEAR_ChannelFile')) {
  83531. require_once 'phar://go-pear.phar/' . 'PEAR/ChannelFile.php';
  83532. }
  83533. $doc_channel = new PEAR_ChannelFile;
  83534. $doc_channel->setServer('doc.php.net');
  83535. $doc_channel->setAlias('phpdocs');
  83536. $doc_channel->setSummary('PHP Documentation Team');
  83537. $doc_channel->setDefaultPEARProtocols();
  83538. $doc_channel->setBaseURL('REST1.0', 'http://doc.php.net/rest/');
  83539. $doc_channel->setBaseURL('REST1.1', 'http://doc.php.net/rest/');
  83540. $doc_channel->setBaseURL('REST1.3', 'http://doc.php.net/rest/');
  83541. return $doc_channel;
  83542. }
  83543. if ($this->_getChannelFromAlias($channel) == '__uri') {
  83544. // the registry is not properly set up, so use defaults
  83545. if (!class_exists('PEAR_ChannelFile')) {
  83546. require_once 'phar://go-pear.phar/' . 'PEAR/ChannelFile.php';
  83547. }
  83548. $private = new PEAR_ChannelFile;
  83549. $private->setName('__uri');
  83550. $private->setDefaultPEARProtocols();
  83551. $private->setBaseURL('REST1.0', '****');
  83552. $private->setSummary('Pseudo-channel for static packages');
  83553. return $private;
  83554. }
  83555. return $ch;
  83556. }
  83557. /**
  83558. * @param string Package name
  83559. * @param string Channel name
  83560. * @return bool
  83561. */
  83562. function packageExists($package, $channel = 'pear.php.net')
  83563. {
  83564. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  83565. return $e;
  83566. }
  83567. $ret = $this->_packageExists($package, $channel);
  83568. $this->_unlock();
  83569. return $ret;
  83570. }
  83571. // }}}
  83572. // {{{ channelExists()
  83573. /**
  83574. * @param string channel name
  83575. * @param bool if true, then aliases will be ignored
  83576. * @return bool
  83577. */
  83578. function channelExists($channel, $noaliases = false)
  83579. {
  83580. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  83581. return $e;
  83582. }
  83583. $ret = $this->_channelExists($channel, $noaliases);
  83584. $this->_unlock();
  83585. return $ret;
  83586. }
  83587. // }}}
  83588. /**
  83589. * @param string channel name mirror is in
  83590. * @param string mirror name
  83591. *
  83592. * @return bool
  83593. */
  83594. function mirrorExists($channel, $mirror)
  83595. {
  83596. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  83597. return $e;
  83598. }
  83599. $ret = $this->_mirrorExists($channel, $mirror);
  83600. $this->_unlock();
  83601. return $ret;
  83602. }
  83603. // {{{ isAlias()
  83604. /**
  83605. * Determines whether the parameter is an alias of a channel
  83606. * @param string
  83607. * @return bool
  83608. */
  83609. function isAlias($alias)
  83610. {
  83611. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  83612. return $e;
  83613. }
  83614. $ret = $this->_isChannelAlias($alias);
  83615. $this->_unlock();
  83616. return $ret;
  83617. }
  83618. // }}}
  83619. // {{{ packageInfo()
  83620. /**
  83621. * @param string|null
  83622. * @param string|null
  83623. * @param string
  83624. * @return array|null
  83625. */
  83626. function packageInfo($package = null, $key = null, $channel = 'pear.php.net')
  83627. {
  83628. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  83629. return $e;
  83630. }
  83631. $ret = $this->_packageInfo($package, $key, $channel);
  83632. $this->_unlock();
  83633. return $ret;
  83634. }
  83635. // }}}
  83636. // {{{ channelInfo()
  83637. /**
  83638. * Retrieve a raw array of channel data.
  83639. *
  83640. * Do not use this, instead use {@link getChannel()} for normal
  83641. * operations. Array structure is undefined in this method
  83642. * @param string channel name
  83643. * @param bool whether to strictly retrieve information only on non-aliases
  83644. * @return array|null|PEAR_Error
  83645. */
  83646. function channelInfo($channel = null, $noaliases = false)
  83647. {
  83648. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  83649. return $e;
  83650. }
  83651. $ret = $this->_channelInfo($channel, $noaliases);
  83652. $this->_unlock();
  83653. return $ret;
  83654. }
  83655. // }}}
  83656. /**
  83657. * @param string
  83658. */
  83659. function channelName($channel)
  83660. {
  83661. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  83662. return $e;
  83663. }
  83664. $ret = $this->_getChannelFromAlias($channel);
  83665. $this->_unlock();
  83666. return $ret;
  83667. }
  83668. /**
  83669. * @param string
  83670. */
  83671. function channelAlias($channel)
  83672. {
  83673. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  83674. return $e;
  83675. }
  83676. $ret = $this->_getAlias($channel);
  83677. $this->_unlock();
  83678. return $ret;
  83679. }
  83680. // {{{ listPackages()
  83681. function listPackages($channel = false)
  83682. {
  83683. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  83684. return $e;
  83685. }
  83686. $ret = $this->_listPackages($channel);
  83687. $this->_unlock();
  83688. return $ret;
  83689. }
  83690. // }}}
  83691. // {{{ listAllPackages()
  83692. function listAllPackages()
  83693. {
  83694. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  83695. return $e;
  83696. }
  83697. $ret = $this->_listAllPackages();
  83698. $this->_unlock();
  83699. return $ret;
  83700. }
  83701. // }}}
  83702. // {{{ listChannel()
  83703. function listChannels()
  83704. {
  83705. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  83706. return $e;
  83707. }
  83708. $ret = $this->_listChannels();
  83709. $this->_unlock();
  83710. return $ret;
  83711. }
  83712. // }}}
  83713. // {{{ addPackage()
  83714. /**
  83715. * Add an installed package to the registry
  83716. * @param string|PEAR_PackageFile_v1|PEAR_PackageFile_v2 package name or object
  83717. * that will be passed to {@link addPackage2()}
  83718. * @param array package info (parsed by PEAR_Common::infoFrom*() methods)
  83719. * @return bool success of saving
  83720. */
  83721. function addPackage($package, $info)
  83722. {
  83723. if (is_object($info)) {
  83724. return $this->addPackage2($info);
  83725. }
  83726. if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  83727. return $e;
  83728. }
  83729. $ret = $this->_addPackage($package, $info);
  83730. $this->_unlock();
  83731. if ($ret) {
  83732. if (!class_exists('PEAR_PackageFile_v1')) {
  83733. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/v1.php';
  83734. }
  83735. $pf = new PEAR_PackageFile_v1;
  83736. $pf->setConfig($this->_config);
  83737. $pf->fromArray($info);
  83738. $this->_dependencyDB->uninstallPackage($pf);
  83739. $this->_dependencyDB->installPackage($pf);
  83740. }
  83741. return $ret;
  83742. }
  83743. // }}}
  83744. // {{{ addPackage2()
  83745. function addPackage2($info)
  83746. {
  83747. if (!is_object($info)) {
  83748. return $this->addPackage($info['package'], $info);
  83749. }
  83750. if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  83751. return $e;
  83752. }
  83753. $ret = $this->_addPackage2($info);
  83754. $this->_unlock();
  83755. if ($ret) {
  83756. $this->_dependencyDB->uninstallPackage($info);
  83757. $this->_dependencyDB->installPackage($info);
  83758. }
  83759. return $ret;
  83760. }
  83761. // }}}
  83762. // {{{ updateChannel()
  83763. /**
  83764. * For future expandibility purposes, separate this
  83765. * @param PEAR_ChannelFile
  83766. */
  83767. function updateChannel($channel, $lastmodified = null)
  83768. {
  83769. if ($channel->getName() == '__uri') {
  83770. return false;
  83771. }
  83772. return $this->addChannel($channel, $lastmodified, true);
  83773. }
  83774. // }}}
  83775. // {{{ deleteChannel()
  83776. /**
  83777. * Deletion fails if there are any packages installed from the channel
  83778. * @param string|PEAR_ChannelFile channel name
  83779. * @return boolean|PEAR_Error True on deletion, false if it doesn't exist
  83780. */
  83781. function deleteChannel($channel)
  83782. {
  83783. if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  83784. return $e;
  83785. }
  83786. $ret = $this->_deleteChannel($channel);
  83787. $this->_unlock();
  83788. if ($ret && is_a($this->_config, 'PEAR_Config')) {
  83789. $this->_config->setChannels($this->listChannels());
  83790. }
  83791. return $ret;
  83792. }
  83793. // }}}
  83794. // {{{ addChannel()
  83795. /**
  83796. * @param PEAR_ChannelFile Channel object
  83797. * @param string Last-Modified header from HTTP for caching
  83798. * @return boolean|PEAR_Error True on creation, false if it already exists
  83799. */
  83800. function addChannel($channel, $lastmodified = false, $update = false)
  83801. {
  83802. if (!is_a($channel, 'PEAR_ChannelFile') || !$channel->validate()) {
  83803. return false;
  83804. }
  83805. if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  83806. return $e;
  83807. }
  83808. $ret = $this->_addChannel($channel, $update, $lastmodified);
  83809. $this->_unlock();
  83810. if (!$update && $ret && is_a($this->_config, 'PEAR_Config')) {
  83811. $this->_config->setChannels($this->listChannels());
  83812. }
  83813. return $ret;
  83814. }
  83815. // }}}
  83816. // {{{ deletePackage()
  83817. function deletePackage($package, $channel = 'pear.php.net')
  83818. {
  83819. if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  83820. return $e;
  83821. }
  83822. $file = $this->_packageFileName($package, $channel);
  83823. $ret = file_exists($file) ? @unlink($file) : false;
  83824. $this->_rebuildFileMap();
  83825. $this->_unlock();
  83826. $p = array('channel' => $channel, 'package' => $package);
  83827. $this->_dependencyDB->uninstallPackage($p);
  83828. return $ret;
  83829. }
  83830. // }}}
  83831. // {{{ updatePackage()
  83832. function updatePackage($package, $info, $merge = true)
  83833. {
  83834. if (is_object($info)) {
  83835. return $this->updatePackage2($info, $merge);
  83836. }
  83837. if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  83838. return $e;
  83839. }
  83840. $ret = $this->_updatePackage($package, $info, $merge);
  83841. $this->_unlock();
  83842. if ($ret) {
  83843. if (!class_exists('PEAR_PackageFile_v1')) {
  83844. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/v1.php';
  83845. }
  83846. $pf = new PEAR_PackageFile_v1;
  83847. $pf->setConfig($this->_config);
  83848. $pf->fromArray($this->packageInfo($package));
  83849. $this->_dependencyDB->uninstallPackage($pf);
  83850. $this->_dependencyDB->installPackage($pf);
  83851. }
  83852. return $ret;
  83853. }
  83854. // }}}
  83855. // {{{ updatePackage2()
  83856. function updatePackage2($info)
  83857. {
  83858. if (!is_object($info)) {
  83859. return $this->updatePackage($info['package'], $info, $merge);
  83860. }
  83861. if (!$info->validate(PEAR_VALIDATE_DOWNLOADING)) {
  83862. return false;
  83863. }
  83864. if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
  83865. return $e;
  83866. }
  83867. $ret = $this->_updatePackage2($info);
  83868. $this->_unlock();
  83869. if ($ret) {
  83870. $this->_dependencyDB->uninstallPackage($info);
  83871. $this->_dependencyDB->installPackage($info);
  83872. }
  83873. return $ret;
  83874. }
  83875. // }}}
  83876. // {{{ getChannel()
  83877. /**
  83878. * @param string channel name
  83879. * @param bool whether to strictly return raw channels (no aliases)
  83880. * @return PEAR_ChannelFile|PEAR_Error
  83881. */
  83882. function getChannel($channel, $noaliases = false)
  83883. {
  83884. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  83885. return $e;
  83886. }
  83887. $ret = $this->_getChannel($channel, $noaliases);
  83888. $this->_unlock();
  83889. if (!$ret) {
  83890. return PEAR::raiseError('Unknown channel: ' . $channel);
  83891. }
  83892. return $ret;
  83893. }
  83894. // }}}
  83895. // {{{ getPackage()
  83896. /**
  83897. * @param string package name
  83898. * @param string channel name
  83899. * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null
  83900. */
  83901. function &getPackage($package, $channel = 'pear.php.net')
  83902. {
  83903. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  83904. return $e;
  83905. }
  83906. $pf = &$this->_getPackage($package, $channel);
  83907. $this->_unlock();
  83908. return $pf;
  83909. }
  83910. // }}}
  83911. /**
  83912. * Get PEAR_PackageFile_v[1/2] objects representing the contents of
  83913. * a dependency group that are installed.
  83914. *
  83915. * This is used at uninstall-time
  83916. * @param array
  83917. * @return array|false
  83918. */
  83919. function getInstalledGroup($group)
  83920. {
  83921. $ret = array();
  83922. if (isset($group['package'])) {
  83923. if (!isset($group['package'][0])) {
  83924. $group['package'] = array($group['package']);
  83925. }
  83926. foreach ($group['package'] as $package) {
  83927. $depchannel = isset($package['channel']) ? $package['channel'] : '__uri';
  83928. $p = &$this->getPackage($package['name'], $depchannel);
  83929. if ($p) {
  83930. $save = &$p;
  83931. $ret[] = &$save;
  83932. }
  83933. }
  83934. }
  83935. if (isset($group['subpackage'])) {
  83936. if (!isset($group['subpackage'][0])) {
  83937. $group['subpackage'] = array($group['subpackage']);
  83938. }
  83939. foreach ($group['subpackage'] as $package) {
  83940. $depchannel = isset($package['channel']) ? $package['channel'] : '__uri';
  83941. $p = &$this->getPackage($package['name'], $depchannel);
  83942. if ($p) {
  83943. $save = &$p;
  83944. $ret[] = &$save;
  83945. }
  83946. }
  83947. }
  83948. if (!count($ret)) {
  83949. return false;
  83950. }
  83951. return $ret;
  83952. }
  83953. // {{{ getChannelValidator()
  83954. /**
  83955. * @param string channel name
  83956. * @return PEAR_Validate|false
  83957. */
  83958. function &getChannelValidator($channel)
  83959. {
  83960. $chan = $this->getChannel($channel);
  83961. if (PEAR::isError($chan)) {
  83962. return $chan;
  83963. }
  83964. $val = $chan->getValidationObject();
  83965. return $val;
  83966. }
  83967. // }}}
  83968. // {{{ getChannels()
  83969. /**
  83970. * @param string channel name
  83971. * @return array an array of PEAR_ChannelFile objects representing every installed channel
  83972. */
  83973. function &getChannels()
  83974. {
  83975. $ret = array();
  83976. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  83977. return $e;
  83978. }
  83979. foreach ($this->_listChannels() as $channel) {
  83980. $e = &$this->_getChannel($channel);
  83981. if (!$e || PEAR::isError($e)) {
  83982. continue;
  83983. }
  83984. $ret[] = $e;
  83985. }
  83986. $this->_unlock();
  83987. return $ret;
  83988. }
  83989. // }}}
  83990. // {{{ checkFileMap()
  83991. /**
  83992. * Test whether a file or set of files belongs to a package.
  83993. *
  83994. * If an array is passed in
  83995. * @param string|array file path, absolute or relative to the pear
  83996. * install dir
  83997. * @param string|array name of PEAR package or array('package' => name, 'channel' =>
  83998. * channel) of a package that will be ignored
  83999. * @param string API version - 1.1 will exclude any files belonging to a package
  84000. * @param array private recursion variable
  84001. * @return array|false which package and channel the file belongs to, or an empty
  84002. * string if the file does not belong to an installed package,
  84003. * or belongs to the second parameter's package
  84004. */
  84005. function checkFileMap($path, $package = false, $api = '1.0', $attrs = false)
  84006. {
  84007. if (is_array($path)) {
  84008. static $notempty;
  84009. if (empty($notempty)) {
  84010. if (!class_exists('PEAR_Installer_Role')) {
  84011. require_once 'phar://go-pear.phar/' . 'PEAR/Installer/Role.php';
  84012. }
  84013. $notempty = function($a) { return !empty($a); };
  84014. }
  84015. $package = is_array($package) ? array(strtolower($package[0]), strtolower($package[1]))
  84016. : strtolower($package);
  84017. $pkgs = array();
  84018. foreach ($path as $name => $attrs) {
  84019. if (is_array($attrs)) {
  84020. if (isset($attrs['install-as'])) {
  84021. $name = $attrs['install-as'];
  84022. }
  84023. if (!in_array($attrs['role'], PEAR_Installer_Role::getInstallableRoles())) {
  84024. // these are not installed
  84025. continue;
  84026. }
  84027. if (!in_array($attrs['role'], PEAR_Installer_Role::getBaseinstallRoles())) {
  84028. $attrs['baseinstalldir'] = is_array($package) ? $package[1] : $package;
  84029. }
  84030. if (isset($attrs['baseinstalldir'])) {
  84031. $name = $attrs['baseinstalldir'] . DIRECTORY_SEPARATOR . $name;
  84032. }
  84033. }
  84034. $pkgs[$name] = $this->checkFileMap($name, $package, $api, $attrs);
  84035. if (PEAR::isError($pkgs[$name])) {
  84036. return $pkgs[$name];
  84037. }
  84038. }
  84039. return array_filter($pkgs, $notempty);
  84040. }
  84041. if (empty($this->filemap_cache)) {
  84042. if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
  84043. return $e;
  84044. }
  84045. $err = $this->_readFileMap();
  84046. $this->_unlock();
  84047. if (PEAR::isError($err)) {
  84048. return $err;
  84049. }
  84050. }
  84051. if (!$attrs) {
  84052. $attrs = array('role' => 'php'); // any old call would be for PHP role only
  84053. }
  84054. if (isset($this->filemap_cache[$attrs['role']][$path])) {
  84055. if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) {
  84056. return false;
  84057. }
  84058. return $this->filemap_cache[$attrs['role']][$path];
  84059. }
  84060. $l = strlen($this->install_dir);
  84061. if (substr($path, 0, $l) == $this->install_dir) {
  84062. $path = preg_replace('!^'.DIRECTORY_SEPARATOR.'+!', '', substr($path, $l));
  84063. }
  84064. if (isset($this->filemap_cache[$attrs['role']][$path])) {
  84065. if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) {
  84066. return false;
  84067. }
  84068. return $this->filemap_cache[$attrs['role']][$path];
  84069. }
  84070. return false;
  84071. }
  84072. // }}}
  84073. // {{{ flush()
  84074. /**
  84075. * Force a reload of the filemap
  84076. * @since 1.5.0RC3
  84077. */
  84078. function flushFileMap()
  84079. {
  84080. $this->filemap_cache = null;
  84081. clearstatcache(); // ensure that the next read gets the full, current filemap
  84082. }
  84083. // }}}
  84084. // {{{ apiVersion()
  84085. /**
  84086. * Get the expected API version. Channels API is version 1.1, as it is backwards
  84087. * compatible with 1.0
  84088. * @return string
  84089. */
  84090. function apiVersion()
  84091. {
  84092. return '1.1';
  84093. }
  84094. // }}}
  84095. /**
  84096. * Parse a package name, or validate a parsed package name array
  84097. * @param string|array pass in an array of format
  84098. * array(
  84099. * 'package' => 'pname',
  84100. * ['channel' => 'channame',]
  84101. * ['version' => 'version',]
  84102. * ['state' => 'state',]
  84103. * ['group' => 'groupname'])
  84104. * or a string of format
  84105. * [channel://][channame/]pname[-version|-state][/group=groupname]
  84106. * @return array|PEAR_Error
  84107. */
  84108. function parsePackageName($param, $defaultchannel = 'pear.php.net')
  84109. {
  84110. $saveparam = $param;
  84111. if (is_array($param)) {
  84112. // convert to string for error messages
  84113. $saveparam = $this->parsedPackageNameToString($param);
  84114. // process the array
  84115. if (!isset($param['package'])) {
  84116. return PEAR::raiseError('parsePackageName(): array $param ' .
  84117. 'must contain a valid package name in index "param"',
  84118. 'package', null, null, $param);
  84119. }
  84120. if (!isset($param['uri'])) {
  84121. if (!isset($param['channel'])) {
  84122. $param['channel'] = $defaultchannel;
  84123. }
  84124. } else {
  84125. $param['channel'] = '__uri';
  84126. }
  84127. } else {
  84128. $components = @parse_url((string) $param);
  84129. if (isset($components['scheme'])) {
  84130. if ($components['scheme'] == 'http') {
  84131. // uri package
  84132. $param = array('uri' => $param, 'channel' => '__uri');
  84133. } elseif($components['scheme'] != 'channel') {
  84134. return PEAR::raiseError('parsePackageName(): only channel:// uris may ' .
  84135. 'be downloaded, not "' . $param . '"', 'invalid', null, null, $param);
  84136. }
  84137. }
  84138. if (!isset($components['path'])) {
  84139. return PEAR::raiseError('parsePackageName(): array $param ' .
  84140. 'must contain a valid package name in "' . $param . '"',
  84141. 'package', null, null, $param);
  84142. }
  84143. if (isset($components['host'])) {
  84144. // remove the leading "/"
  84145. $components['path'] = substr($components['path'], 1);
  84146. }
  84147. if (!isset($components['scheme'])) {
  84148. if (strpos($components['path'], '/') !== false) {
  84149. if ($components['path'][0] == '/') {
  84150. return PEAR::raiseError('parsePackageName(): this is not ' .
  84151. 'a package name, it begins with "/" in "' . $param . '"',
  84152. 'invalid', null, null, $param);
  84153. }
  84154. $parts = explode('/', $components['path']);
  84155. $components['host'] = array_shift($parts);
  84156. if (count($parts) > 1) {
  84157. $components['path'] = array_pop($parts);
  84158. $components['host'] .= '/' . implode('/', $parts);
  84159. } else {
  84160. $components['path'] = implode('/', $parts);
  84161. }
  84162. } else {
  84163. $components['host'] = $defaultchannel;
  84164. }
  84165. } else {
  84166. if (strpos($components['path'], '/')) {
  84167. $parts = explode('/', $components['path']);
  84168. $components['path'] = array_pop($parts);
  84169. $components['host'] .= '/' . implode('/', $parts);
  84170. }
  84171. }
  84172. if (is_array($param)) {
  84173. $param['package'] = $components['path'];
  84174. } else {
  84175. $param = array(
  84176. 'package' => $components['path']
  84177. );
  84178. if (isset($components['host'])) {
  84179. $param['channel'] = $components['host'];
  84180. }
  84181. }
  84182. if (isset($components['fragment'])) {
  84183. $param['group'] = $components['fragment'];
  84184. }
  84185. if (isset($components['user'])) {
  84186. $param['user'] = $components['user'];
  84187. }
  84188. if (isset($components['pass'])) {
  84189. $param['pass'] = $components['pass'];
  84190. }
  84191. if (isset($components['query'])) {
  84192. parse_str($components['query'], $param['opts']);
  84193. }
  84194. // check for extension
  84195. $pathinfo = pathinfo($param['package']);
  84196. if (isset($pathinfo['extension']) &&
  84197. in_array(strtolower($pathinfo['extension']), array('tgz', 'tar'))) {
  84198. $param['extension'] = $pathinfo['extension'];
  84199. $param['package'] = substr($pathinfo['basename'], 0,
  84200. strlen($pathinfo['basename']) - 4);
  84201. }
  84202. // check for version
  84203. if (strpos($param['package'], '-')) {
  84204. $test = explode('-', $param['package']);
  84205. if (count($test) != 2) {
  84206. return PEAR::raiseError('parsePackageName(): only one version/state ' .
  84207. 'delimiter "-" is allowed in "' . $saveparam . '"',
  84208. 'version', null, null, $param);
  84209. }
  84210. list($param['package'], $param['version']) = $test;
  84211. }
  84212. }
  84213. // validation
  84214. $info = $this->channelExists($param['channel']);
  84215. if (PEAR::isError($info)) {
  84216. return $info;
  84217. }
  84218. if (!$info) {
  84219. return PEAR::raiseError('unknown channel "' . $param['channel'] .
  84220. '" in "' . $saveparam . '"', 'channel', null, null, $param);
  84221. }
  84222. $chan = $this->getChannel($param['channel']);
  84223. if (PEAR::isError($chan)) {
  84224. return $chan;
  84225. }
  84226. if (!$chan) {
  84227. return PEAR::raiseError("Exception: corrupt registry, could not " .
  84228. "retrieve channel " . $param['channel'] . " information",
  84229. 'registry', null, null, $param);
  84230. }
  84231. $param['channel'] = $chan->getName();
  84232. $validate = $chan->getValidationObject();
  84233. $vpackage = $chan->getValidationPackage();
  84234. // validate package name
  84235. if (!$validate->validPackageName($param['package'], $vpackage['_content'])) {
  84236. return PEAR::raiseError('parsePackageName(): invalid package name "' .
  84237. $param['package'] . '" in "' . $saveparam . '"',
  84238. 'package', null, null, $param);
  84239. }
  84240. if (isset($param['group'])) {
  84241. if (!PEAR_Validate::validGroupName($param['group'])) {
  84242. return PEAR::raiseError('parsePackageName(): dependency group "' . $param['group'] .
  84243. '" is not a valid group name in "' . $saveparam . '"', 'group', null, null,
  84244. $param);
  84245. }
  84246. }
  84247. if (isset($param['state'])) {
  84248. if (!in_array(strtolower($param['state']), $validate->getValidStates())) {
  84249. return PEAR::raiseError('parsePackageName(): state "' . $param['state']
  84250. . '" is not a valid state in "' . $saveparam . '"',
  84251. 'state', null, null, $param);
  84252. }
  84253. }
  84254. if (isset($param['version'])) {
  84255. if (isset($param['state'])) {
  84256. return PEAR::raiseError('parsePackageName(): cannot contain both ' .
  84257. 'a version and a stability (state) in "' . $saveparam . '"',
  84258. 'version/state', null, null, $param);
  84259. }
  84260. // check whether version is actually a state
  84261. if (in_array(strtolower($param['version']), $validate->getValidStates())) {
  84262. $param['state'] = strtolower($param['version']);
  84263. unset($param['version']);
  84264. } else {
  84265. if (!$validate->validVersion($param['version'])) {
  84266. return PEAR::raiseError('parsePackageName(): "' . $param['version'] .
  84267. '" is neither a valid version nor a valid state in "' .
  84268. $saveparam . '"', 'version/state', null, null, $param);
  84269. }
  84270. }
  84271. }
  84272. return $param;
  84273. }
  84274. /**
  84275. * @param array
  84276. * @return string
  84277. */
  84278. function parsedPackageNameToString($parsed, $brief = false)
  84279. {
  84280. if (is_string($parsed)) {
  84281. return $parsed;
  84282. }
  84283. if (is_object($parsed)) {
  84284. $p = $parsed;
  84285. $parsed = array(
  84286. 'package' => $p->getPackage(),
  84287. 'channel' => $p->getChannel(),
  84288. 'version' => $p->getVersion(),
  84289. );
  84290. }
  84291. if (isset($parsed['uri'])) {
  84292. return $parsed['uri'];
  84293. }
  84294. if ($brief) {
  84295. if ($channel = $this->channelAlias($parsed['channel'])) {
  84296. return $channel . '/' . $parsed['package'];
  84297. }
  84298. }
  84299. $upass = '';
  84300. if (isset($parsed['user'])) {
  84301. $upass = $parsed['user'];
  84302. if (isset($parsed['pass'])) {
  84303. $upass .= ':' . $parsed['pass'];
  84304. }
  84305. $upass = "$upass@";
  84306. }
  84307. $ret = 'channel://' . $upass . $parsed['channel'] . '/' . $parsed['package'];
  84308. if (isset($parsed['version']) || isset($parsed['state'])) {
  84309. $ver = isset($parsed['version']) ? $parsed['version'] : '';
  84310. $ver .= isset($parsed['state']) ? $parsed['state'] : '';
  84311. $ret .= '-' . $ver;
  84312. }
  84313. if (isset($parsed['extension'])) {
  84314. $ret .= '.' . $parsed['extension'];
  84315. }
  84316. if (isset($parsed['opts'])) {
  84317. $ret .= '?';
  84318. foreach ($parsed['opts'] as $name => $value) {
  84319. $parsed['opts'][$name] = "$name=$value";
  84320. }
  84321. $ret .= implode('&', $parsed['opts']);
  84322. }
  84323. if (isset($parsed['group'])) {
  84324. $ret .= '#' . $parsed['group'];
  84325. }
  84326. return $ret;
  84327. }
  84328. }
  84329. <?php
  84330. /**
  84331. * PEAR_REST
  84332. *
  84333. * PHP versions 4 and 5
  84334. *
  84335. * @category pear
  84336. * @package PEAR
  84337. * @author Greg Beaver <cellog@php.net>
  84338. * @copyright 1997-2009 The Authors
  84339. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  84340. * @link http://pear.php.net/package/PEAR
  84341. * @since File available since Release 1.4.0a1
  84342. */
  84343. /**
  84344. * For downloading xml files
  84345. */
  84346. require_once 'phar://go-pear.phar/' . 'PEAR.php';
  84347. require_once 'phar://go-pear.phar/' . 'PEAR/XMLParser.php';
  84348. require_once 'phar://go-pear.phar/' . 'PEAR/Proxy.php';
  84349. /**
  84350. * Intelligently retrieve data, following hyperlinks if necessary, and re-directing
  84351. * as well
  84352. * @category pear
  84353. * @package PEAR
  84354. * @author Greg Beaver <cellog@php.net>
  84355. * @copyright 1997-2009 The Authors
  84356. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  84357. * @version Release: 1.10.16
  84358. * @link http://pear.php.net/package/PEAR
  84359. * @since Class available since Release 1.4.0a1
  84360. */
  84361. class PEAR_REST
  84362. {
  84363. var $config;
  84364. var $_options;
  84365. function __construct(&$config, $options = array())
  84366. {
  84367. $this->config = &$config;
  84368. $this->_options = $options;
  84369. }
  84370. /**
  84371. * Retrieve REST data, but always retrieve the local cache if it is available.
  84372. *
  84373. * This is useful for elements that should never change, such as information on a particular
  84374. * release
  84375. * @param string full URL to this resource
  84376. * @param array|false contents of the accept-encoding header
  84377. * @param boolean if true, xml will be returned as a string, otherwise, xml will be
  84378. * parsed using PEAR_XMLParser
  84379. * @return string|array
  84380. */
  84381. function retrieveCacheFirst($url, $accept = false, $forcestring = false, $channel = false)
  84382. {
  84383. $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
  84384. md5($url) . 'rest.cachefile';
  84385. if (file_exists($cachefile)) {
  84386. return unserialize(implode('', file($cachefile)));
  84387. }
  84388. return $this->retrieveData($url, $accept, $forcestring, $channel);
  84389. }
  84390. /**
  84391. * Retrieve a remote REST resource
  84392. * @param string full URL to this resource
  84393. * @param array|false contents of the accept-encoding header
  84394. * @param boolean if true, xml will be returned as a string, otherwise, xml will be
  84395. * parsed using PEAR_XMLParser
  84396. * @return string|array
  84397. */
  84398. function retrieveData($url, $accept = false, $forcestring = false, $channel = false)
  84399. {
  84400. $cacheId = $this->getCacheId($url);
  84401. if ($ret = $this->useLocalCache($url, $cacheId)) {
  84402. return $ret;
  84403. }
  84404. $file = $trieddownload = false;
  84405. if (!isset($this->_options['offline'])) {
  84406. $trieddownload = true;
  84407. $file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false, $accept, $channel);
  84408. }
  84409. if (PEAR::isError($file)) {
  84410. if ($file->getCode() !== -9276) {
  84411. return $file;
  84412. }
  84413. $trieddownload = false;
  84414. $file = false; // use local copy if available on socket connect error
  84415. }
  84416. if (!$file) {
  84417. $ret = $this->getCache($url);
  84418. if (!PEAR::isError($ret) && $trieddownload) {
  84419. // reset the age of the cache if the server says it was unmodified
  84420. $result = $this->saveCache($url, $ret, null, true, $cacheId);
  84421. if (PEAR::isError($result)) {
  84422. return PEAR::raiseError($result->getMessage());
  84423. }
  84424. }
  84425. return $ret;
  84426. }
  84427. if (is_array($file)) {
  84428. $headers = $file[2];
  84429. $lastmodified = $file[1];
  84430. $content = $file[0];
  84431. } else {
  84432. $headers = array();
  84433. $lastmodified = false;
  84434. $content = $file;
  84435. }
  84436. if ($forcestring) {
  84437. $result = $this->saveCache($url, $content, $lastmodified, false, $cacheId);
  84438. if (PEAR::isError($result)) {
  84439. return PEAR::raiseError($result->getMessage());
  84440. }
  84441. return $content;
  84442. }
  84443. if (isset($headers['content-type'])) {
  84444. $content_type = explode(";", $headers['content-type']);
  84445. $content_type = $content_type[0];
  84446. switch ($content_type) {
  84447. case 'text/xml' :
  84448. case 'application/xml' :
  84449. case 'text/plain' :
  84450. if ($content_type === 'text/plain') {
  84451. $check = substr($content, 0, 5);
  84452. if ($check !== '<?xml') {
  84453. break;
  84454. }
  84455. }
  84456. $parser = new PEAR_XMLParser;
  84457. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  84458. $err = $parser->parse($content);
  84459. PEAR::popErrorHandling();
  84460. if (PEAR::isError($err)) {
  84461. return PEAR::raiseError('Invalid xml downloaded from "' . $url . '": ' .
  84462. $err->getMessage());
  84463. }
  84464. $content = $parser->getData();
  84465. case 'text/html' :
  84466. default :
  84467. // use it as a string
  84468. }
  84469. } else {
  84470. // assume XML
  84471. $parser = new PEAR_XMLParser;
  84472. $parser->parse($content);
  84473. $content = $parser->getData();
  84474. }
  84475. $result = $this->saveCache($url, $content, $lastmodified, false, $cacheId);
  84476. if (PEAR::isError($result)) {
  84477. return PEAR::raiseError($result->getMessage());
  84478. }
  84479. return $content;
  84480. }
  84481. function useLocalCache($url, $cacheid = null)
  84482. {
  84483. if (!is_array($cacheid)) {
  84484. $cacheid = $this->getCacheId($url);
  84485. }
  84486. $cachettl = $this->config->get('cache_ttl');
  84487. // If cache is newer than $cachettl seconds, we use the cache!
  84488. if (is_array($cacheid) && time() - $cacheid['age'] < $cachettl) {
  84489. return $this->getCache($url);
  84490. }
  84491. return false;
  84492. }
  84493. /**
  84494. * @param string $url
  84495. *
  84496. * @return bool|mixed
  84497. */
  84498. function getCacheId($url)
  84499. {
  84500. $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
  84501. md5($url) . 'rest.cacheid';
  84502. if (!file_exists($cacheidfile)) {
  84503. return false;
  84504. }
  84505. $ret = unserialize(implode('', file($cacheidfile)));
  84506. return $ret;
  84507. }
  84508. function getCache($url)
  84509. {
  84510. $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
  84511. md5($url) . 'rest.cachefile';
  84512. if (!file_exists($cachefile)) {
  84513. return PEAR::raiseError('No cached content available for "' . $url . '"');
  84514. }
  84515. return unserialize(implode('', file($cachefile)));
  84516. }
  84517. /**
  84518. * @param string full URL to REST resource
  84519. * @param string original contents of the REST resource
  84520. * @param array HTTP Last-Modified and ETag headers
  84521. * @param bool if true, then the cache id file should be regenerated to
  84522. * trigger a new time-to-live value
  84523. */
  84524. function saveCache($url, $contents, $lastmodified, $nochange = false, $cacheid = null)
  84525. {
  84526. $cache_dir = $this->config->get('cache_dir');
  84527. $d = $cache_dir . DIRECTORY_SEPARATOR . md5($url);
  84528. $cacheidfile = $d . 'rest.cacheid';
  84529. $cachefile = $d . 'rest.cachefile';
  84530. if (!is_dir($cache_dir)) {
  84531. if (System::mkdir(array('-p', $cache_dir)) === false) {
  84532. return PEAR::raiseError("The value of config option cache_dir ($cache_dir) is not a directory and attempts to create the directory failed.");
  84533. }
  84534. }
  84535. if (!is_writeable($cache_dir)) {
  84536. // If writing to the cache dir is not going to work, silently do nothing.
  84537. // An ugly hack, but retains compat with PEAR 1.9.1 where many commands
  84538. // work fine as non-root user (w/out write access to default cache dir).
  84539. return true;
  84540. }
  84541. if ($cacheid === null && $nochange) {
  84542. $cacheid = unserialize(implode('', file($cacheidfile)));
  84543. }
  84544. $idData = serialize(array(
  84545. 'age' => time(),
  84546. 'lastChange' => ($nochange ? $cacheid['lastChange'] : $lastmodified),
  84547. ));
  84548. $result = $this->saveCacheFile($cacheidfile, $idData);
  84549. if (PEAR::isError($result)) {
  84550. return $result;
  84551. } elseif ($nochange) {
  84552. return true;
  84553. }
  84554. $result = $this->saveCacheFile($cachefile, serialize($contents));
  84555. if (PEAR::isError($result)) {
  84556. if (file_exists($cacheidfile)) {
  84557. @unlink($cacheidfile);
  84558. }
  84559. return $result;
  84560. }
  84561. return true;
  84562. }
  84563. function saveCacheFile($file, $contents)
  84564. {
  84565. $len = strlen($contents);
  84566. $cachefile_fp = @fopen($file, 'xb'); // x is the O_CREAT|O_EXCL mode
  84567. if ($cachefile_fp !== false) { // create file
  84568. if (fwrite($cachefile_fp, $contents, $len) < $len) {
  84569. fclose($cachefile_fp);
  84570. return PEAR::raiseError("Could not write $file.");
  84571. }
  84572. } else { // update file
  84573. $cachefile_fp = @fopen($file, 'r+b'); // do not truncate file
  84574. if (!$cachefile_fp) {
  84575. return PEAR::raiseError("Could not open $file for writing.");
  84576. }
  84577. if (OS_WINDOWS) {
  84578. $not_symlink = !is_link($file); // see bug #18834
  84579. } else {
  84580. $cachefile_lstat = lstat($file);
  84581. $cachefile_fstat = fstat($cachefile_fp);
  84582. $not_symlink = $cachefile_lstat['mode'] == $cachefile_fstat['mode']
  84583. && $cachefile_lstat['ino'] == $cachefile_fstat['ino']
  84584. && $cachefile_lstat['dev'] == $cachefile_fstat['dev']
  84585. && $cachefile_fstat['nlink'] === 1;
  84586. }
  84587. if ($not_symlink) {
  84588. ftruncate($cachefile_fp, 0); // NOW truncate
  84589. if (fwrite($cachefile_fp, $contents, $len) < $len) {
  84590. fclose($cachefile_fp);
  84591. return PEAR::raiseError("Could not write $file.");
  84592. }
  84593. } else {
  84594. fclose($cachefile_fp);
  84595. $link = function_exists('readlink') ? readlink($file) : $file;
  84596. return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $file . ' as it is symlinked to ' . $link . ' - Possible symlink attack');
  84597. }
  84598. }
  84599. fclose($cachefile_fp);
  84600. return true;
  84601. }
  84602. /**
  84603. * Efficiently Download a file through HTTP. Returns downloaded file as a string in-memory
  84604. * This is best used for small files
  84605. *
  84606. * If an HTTP proxy has been configured (http_proxy PEAR_Config
  84607. * setting), the proxy will be used.
  84608. *
  84609. * @param string $url the URL to download
  84610. * @param string $save_dir directory to save file in
  84611. * @param false|string|array $lastmodified header values to check against for caching
  84612. * use false to return the header values from this download
  84613. * @param false|array $accept Accept headers to send
  84614. * @return string|array Returns the contents of the downloaded file or a PEAR
  84615. * error on failure. If the error is caused by
  84616. * socket-related errors, the error object will
  84617. * have the fsockopen error code available through
  84618. * getCode(). If caching is requested, then return the header
  84619. * values.
  84620. *
  84621. * @access public
  84622. */
  84623. function downloadHttp($url, $lastmodified = null, $accept = false, $channel = false)
  84624. {
  84625. static $redirect = 0;
  84626. // always reset , so we are clean case of error
  84627. $wasredirect = $redirect;
  84628. $redirect = 0;
  84629. $info = parse_url($url);
  84630. if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) {
  84631. return PEAR::raiseError('Cannot download non-http URL "' . $url . '"');
  84632. }
  84633. if (!isset($info['host'])) {
  84634. return PEAR::raiseError('Cannot download from non-URL "' . $url . '"');
  84635. }
  84636. $host = isset($info['host']) ? $info['host'] : null;
  84637. $port = isset($info['port']) ? $info['port'] : null;
  84638. $path = isset($info['path']) ? $info['path'] : null;
  84639. $schema = (isset($info['scheme']) && $info['scheme'] == 'https') ? 'https' : 'http';
  84640. $proxy = new PEAR_Proxy($this->config);
  84641. if (empty($port)) {
  84642. $port = (isset($info['scheme']) && $info['scheme'] == 'https') ? 443 : 80;
  84643. }
  84644. if ($proxy->isProxyConfigured() && $schema === 'http') {
  84645. $request = "GET $url HTTP/1.1\r\n";
  84646. } else {
  84647. $request = "GET $path HTTP/1.1\r\n";
  84648. }
  84649. $request .= "Host: $host\r\n";
  84650. $ifmodifiedsince = '';
  84651. if (is_array($lastmodified)) {
  84652. if (isset($lastmodified['Last-Modified'])) {
  84653. $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n";
  84654. }
  84655. if (isset($lastmodified['ETag'])) {
  84656. $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";
  84657. }
  84658. } else {
  84659. $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');
  84660. }
  84661. $request .= $ifmodifiedsince .
  84662. "User-Agent: PEAR/1.10.16/PHP/" . PHP_VERSION . "\r\n";
  84663. $username = $this->config->get('username', null, $channel);
  84664. $password = $this->config->get('password', null, $channel);
  84665. if ($username && $password) {
  84666. $tmp = base64_encode("$username:$password");
  84667. $request .= "Authorization: Basic $tmp\r\n";
  84668. }
  84669. $proxyAuth = $proxy->getProxyAuth();
  84670. if ($proxyAuth) {
  84671. $request .= 'Proxy-Authorization: Basic ' .
  84672. $proxyAuth . "\r\n";
  84673. }
  84674. if ($accept) {
  84675. $request .= 'Accept: ' . implode(', ', $accept) . "\r\n";
  84676. }
  84677. $request .= "Accept-Encoding:\r\n";
  84678. $request .= "Connection: close\r\n";
  84679. $request .= "\r\n";
  84680. $secure = ($schema == 'https');
  84681. $fp = $proxy->openSocket($host, $port, $secure);
  84682. if (PEAR::isError($fp)) {
  84683. return $fp;
  84684. }
  84685. fwrite($fp, $request);
  84686. $headers = array();
  84687. $reply = 0;
  84688. while ($line = trim(fgets($fp, 1024))) {
  84689. if (preg_match('/^([^:]+):\s+(.*)\s*\\z/', $line, $matches)) {
  84690. $headers[strtolower($matches[1])] = trim($matches[2]);
  84691. } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
  84692. $reply = (int)$matches[1];
  84693. if ($reply == 304 && ($lastmodified || ($lastmodified === false))) {
  84694. return false;
  84695. }
  84696. if (!in_array($reply, array(200, 301, 302, 303, 305, 307))) {
  84697. return PEAR::raiseError("File $schema://$host:$port$path not valid (received: $line)");
  84698. }
  84699. }
  84700. }
  84701. if ($reply != 200) {
  84702. if (!isset($headers['location'])) {
  84703. return PEAR::raiseError("File $schema://$host:$port$path not valid (redirected but no location)");
  84704. }
  84705. if ($wasredirect > 4) {
  84706. return PEAR::raiseError("File $schema://$host:$port$path not valid (redirection looped more than 5 times)");
  84707. }
  84708. $redirect = $wasredirect + 1;
  84709. return $this->downloadHttp($headers['location'], $lastmodified, $accept, $channel);
  84710. }
  84711. $length = isset($headers['content-length']) ? $headers['content-length'] : -1;
  84712. $data = '';
  84713. while ($chunk = @fread($fp, 8192)) {
  84714. $data .= $chunk;
  84715. }
  84716. fclose($fp);
  84717. if ($lastmodified === false || $lastmodified) {
  84718. if (isset($headers['etag'])) {
  84719. $lastmodified = array('ETag' => $headers['etag']);
  84720. }
  84721. if (isset($headers['last-modified'])) {
  84722. if (is_array($lastmodified)) {
  84723. $lastmodified['Last-Modified'] = $headers['last-modified'];
  84724. } else {
  84725. $lastmodified = $headers['last-modified'];
  84726. }
  84727. }
  84728. return array($data, $lastmodified, $headers);
  84729. }
  84730. return $data;
  84731. }
  84732. }
  84733. <?php
  84734. /**
  84735. * PEAR_REST_10
  84736. *
  84737. * PHP versions 4 and 5
  84738. *
  84739. * @category pear
  84740. * @package PEAR
  84741. * @author Greg Beaver <cellog@php.net>
  84742. * @copyright 1997-2009 The Authors
  84743. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  84744. * @link http://pear.php.net/package/PEAR
  84745. * @since File available since Release 1.4.0a12
  84746. */
  84747. /**
  84748. * For downloading REST xml/txt files
  84749. */
  84750. require_once 'phar://go-pear.phar/' . 'PEAR/REST.php';
  84751. /**
  84752. * Implement REST 1.0
  84753. *
  84754. * @category pear
  84755. * @package PEAR
  84756. * @author Greg Beaver <cellog@php.net>
  84757. * @copyright 1997-2009 The Authors
  84758. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  84759. * @version Release: 1.10.16
  84760. * @link http://pear.php.net/package/PEAR
  84761. * @since Class available since Release 1.4.0a12
  84762. */
  84763. class PEAR_REST_10
  84764. {
  84765. /**
  84766. * @var PEAR_REST
  84767. */
  84768. var $_rest;
  84769. function __construct($config, $options = array())
  84770. {
  84771. $this->_rest = new PEAR_REST($config, $options);
  84772. }
  84773. /**
  84774. * Retrieve information about a remote package to be downloaded from a REST server
  84775. *
  84776. * @param string $base The uri to prepend to all REST calls
  84777. * @param array $packageinfo an array of format:
  84778. * <pre>
  84779. * array(
  84780. * 'package' => 'packagename',
  84781. * 'channel' => 'channelname',
  84782. * ['state' => 'alpha' (or valid state),]
  84783. * -or-
  84784. * ['version' => '1.whatever']
  84785. * </pre>
  84786. * @param string $prefstate Current preferred_state config variable value
  84787. * @param bool $installed the installed version of this package to compare against
  84788. * @return array|false|PEAR_Error see {@link _returnDownloadURL()}
  84789. */
  84790. function getDownloadURL($base, $packageinfo, $prefstate, $installed, $channel = false)
  84791. {
  84792. $states = $this->betterStates($prefstate, true);
  84793. if (!$states) {
  84794. return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
  84795. }
  84796. $channel = $packageinfo['channel'];
  84797. $package = $packageinfo['package'];
  84798. $state = isset($packageinfo['state']) ? $packageinfo['state'] : null;
  84799. $version = isset($packageinfo['version']) ? $packageinfo['version'] : null;
  84800. $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml';
  84801. $info = $this->_rest->retrieveData($restFile, false, false, $channel);
  84802. if (PEAR::isError($info)) {
  84803. return PEAR::raiseError('No releases available for package "' .
  84804. $channel . '/' . $package . '"');
  84805. }
  84806. if (!isset($info['r'])) {
  84807. return false;
  84808. }
  84809. $release = $found = false;
  84810. if (!is_array($info['r']) || !isset($info['r'][0])) {
  84811. $info['r'] = array($info['r']);
  84812. }
  84813. foreach ($info['r'] as $release) {
  84814. if (!isset($this->_rest->_options['force']) && ($installed &&
  84815. version_compare($release['v'], $installed, '<'))) {
  84816. continue;
  84817. }
  84818. if (isset($state)) {
  84819. // try our preferred state first
  84820. if ($release['s'] == $state) {
  84821. $found = true;
  84822. break;
  84823. }
  84824. // see if there is something newer and more stable
  84825. // bug #7221
  84826. if (in_array($release['s'], $this->betterStates($state), true)) {
  84827. $found = true;
  84828. break;
  84829. }
  84830. } elseif (isset($version)) {
  84831. if ($release['v'] == $version) {
  84832. $found = true;
  84833. break;
  84834. }
  84835. } else {
  84836. if (in_array($release['s'], $states)) {
  84837. $found = true;
  84838. break;
  84839. }
  84840. }
  84841. }
  84842. return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel);
  84843. }
  84844. function getDepDownloadURL($base, $xsdversion, $dependency, $deppackage,
  84845. $prefstate = 'stable', $installed = false, $channel = false)
  84846. {
  84847. $states = $this->betterStates($prefstate, true);
  84848. if (!$states) {
  84849. return PEAR::raiseError('"' . $prefstate . '" is not a valid state');
  84850. }
  84851. $channel = $dependency['channel'];
  84852. $package = $dependency['name'];
  84853. $state = isset($dependency['state']) ? $dependency['state'] : null;
  84854. $version = isset($dependency['version']) ? $dependency['version'] : null;
  84855. $restFile = $base . 'r/' . strtolower($package) . '/allreleases.xml';
  84856. $info = $this->_rest->retrieveData($restFile, false, false, $channel);
  84857. if (PEAR::isError($info)) {
  84858. return PEAR::raiseError('Package "' . $deppackage['channel'] . '/' . $deppackage['package']
  84859. . '" dependency "' . $channel . '/' . $package . '" has no releases');
  84860. }
  84861. if (!is_array($info) || !isset($info['r'])) {
  84862. return false;
  84863. }
  84864. $exclude = array();
  84865. $min = $max = $recommended = false;
  84866. if ($xsdversion == '1.0') {
  84867. switch ($dependency['rel']) {
  84868. case 'ge' :
  84869. $min = $dependency['version'];
  84870. break;
  84871. case 'gt' :
  84872. $min = $dependency['version'];
  84873. $exclude = array($dependency['version']);
  84874. break;
  84875. case 'eq' :
  84876. $recommended = $dependency['version'];
  84877. break;
  84878. case 'lt' :
  84879. $max = $dependency['version'];
  84880. $exclude = array($dependency['version']);
  84881. break;
  84882. case 'le' :
  84883. $max = $dependency['version'];
  84884. break;
  84885. case 'ne' :
  84886. $exclude = array($dependency['version']);
  84887. break;
  84888. }
  84889. } else {
  84890. $min = isset($dependency['min']) ? $dependency['min'] : false;
  84891. $max = isset($dependency['max']) ? $dependency['max'] : false;
  84892. $recommended = isset($dependency['recommended']) ?
  84893. $dependency['recommended'] : false;
  84894. if (isset($dependency['exclude'])) {
  84895. if (!isset($dependency['exclude'][0])) {
  84896. $exclude = array($dependency['exclude']);
  84897. }
  84898. }
  84899. }
  84900. $release = $found = false;
  84901. if (!is_array($info['r']) || !isset($info['r'][0])) {
  84902. $info['r'] = array($info['r']);
  84903. }
  84904. foreach ($info['r'] as $release) {
  84905. if (!isset($this->_rest->_options['force']) && ($installed &&
  84906. version_compare($release['v'], $installed, '<'))) {
  84907. continue;
  84908. }
  84909. if (in_array($release['v'], $exclude)) { // skip excluded versions
  84910. continue;
  84911. }
  84912. // allow newer releases to say "I'm OK with the dependent package"
  84913. if ($xsdversion == '2.0' && isset($release['co'])) {
  84914. if (!is_array($release['co']) || !isset($release['co'][0])) {
  84915. $release['co'] = array($release['co']);
  84916. }
  84917. foreach ($release['co'] as $entry) {
  84918. if (isset($entry['x']) && !is_array($entry['x'])) {
  84919. $entry['x'] = array($entry['x']);
  84920. } elseif (!isset($entry['x'])) {
  84921. $entry['x'] = array();
  84922. }
  84923. if ($entry['c'] == $deppackage['channel'] &&
  84924. strtolower($entry['p']) == strtolower($deppackage['package']) &&
  84925. version_compare($deppackage['version'], $entry['min'], '>=') &&
  84926. version_compare($deppackage['version'], $entry['max'], '<=') &&
  84927. !in_array($release['v'], $entry['x'])) {
  84928. $recommended = $release['v'];
  84929. break;
  84930. }
  84931. }
  84932. }
  84933. if ($recommended) {
  84934. if ($release['v'] != $recommended) { // if we want a specific
  84935. // version, then skip all others
  84936. continue;
  84937. } else {
  84938. if (!in_array($release['s'], $states)) {
  84939. // the stability is too low, but we must return the
  84940. // recommended version if possible
  84941. return $this->_returnDownloadURL($base, $package, $release, $info, true, false, $channel);
  84942. }
  84943. }
  84944. }
  84945. if ($min && version_compare($release['v'], $min, 'lt')) { // skip too old versions
  84946. continue;
  84947. }
  84948. if ($max && version_compare($release['v'], $max, 'gt')) { // skip too new versions
  84949. continue;
  84950. }
  84951. if ($installed && version_compare($release['v'], $installed, '<')) {
  84952. continue;
  84953. }
  84954. if (in_array($release['s'], $states)) { // if in the preferred state...
  84955. $found = true; // ... then use it
  84956. break;
  84957. }
  84958. }
  84959. return $this->_returnDownloadURL($base, $package, $release, $info, $found, false, $channel);
  84960. }
  84961. /**
  84962. * Take raw data and return the array needed for processing a download URL
  84963. *
  84964. * @param string $base REST base uri
  84965. * @param string $package Package name
  84966. * @param array $release an array of format array('v' => version, 's' => state)
  84967. * describing the release to download
  84968. * @param array $info list of all releases as defined by allreleases.xml
  84969. * @param bool|null $found determines whether the release was found or this is the next
  84970. * best alternative. If null, then versions were skipped because
  84971. * of PHP dependency
  84972. * @return array|PEAR_Error
  84973. * @access private
  84974. */
  84975. function _returnDownloadURL($base, $package, $release, $info, $found, $phpversion = false, $channel = false)
  84976. {
  84977. if (!$found) {
  84978. $release = $info['r'][0];
  84979. }
  84980. $packageLower = strtolower($package);
  84981. $pinfo = $this->_rest->retrieveCacheFirst($base . 'p/' . $packageLower . '/' .
  84982. 'info.xml', false, false, $channel);
  84983. if (PEAR::isError($pinfo)) {
  84984. return PEAR::raiseError('Package "' . $package .
  84985. '" does not have REST info xml available');
  84986. }
  84987. $releaseinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' .
  84988. $release['v'] . '.xml', false, false, $channel);
  84989. if (PEAR::isError($releaseinfo)) {
  84990. return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] .
  84991. '" does not have REST xml available');
  84992. }
  84993. $packagexml = $this->_rest->retrieveCacheFirst($base . 'r/' . $packageLower . '/' .
  84994. 'deps.' . $release['v'] . '.txt', false, true, $channel);
  84995. if (PEAR::isError($packagexml)) {
  84996. return PEAR::raiseError('Package "' . $package . '" Version "' . $release['v'] .
  84997. '" does not have REST dependency information available');
  84998. }
  84999. $packagexml = unserialize($packagexml);
  85000. if (!$packagexml) {
  85001. $packagexml = array();
  85002. }
  85003. $allinfo = $this->_rest->retrieveData($base . 'r/' . $packageLower .
  85004. '/allreleases.xml', false, false, $channel);
  85005. if (PEAR::isError($allinfo)) {
  85006. return $allinfo;
  85007. }
  85008. if (!is_array($allinfo['r']) || !isset($allinfo['r'][0])) {
  85009. $allinfo['r'] = array($allinfo['r']);
  85010. }
  85011. $compatible = false;
  85012. foreach ($allinfo['r'] as $release) {
  85013. if ($release['v'] != $releaseinfo['v']) {
  85014. continue;
  85015. }
  85016. if (!isset($release['co'])) {
  85017. break;
  85018. }
  85019. $compatible = array();
  85020. if (!is_array($release['co']) || !isset($release['co'][0])) {
  85021. $release['co'] = array($release['co']);
  85022. }
  85023. foreach ($release['co'] as $entry) {
  85024. $comp = array();
  85025. $comp['name'] = $entry['p'];
  85026. $comp['channel'] = $entry['c'];
  85027. $comp['min'] = $entry['min'];
  85028. $comp['max'] = $entry['max'];
  85029. if (isset($entry['x']) && !is_array($entry['x'])) {
  85030. $comp['exclude'] = $entry['x'];
  85031. }
  85032. $compatible[] = $comp;
  85033. }
  85034. if (count($compatible) == 1) {
  85035. $compatible = $compatible[0];
  85036. }
  85037. break;
  85038. }
  85039. $deprecated = false;
  85040. if (isset($pinfo['dc']) && isset($pinfo['dp'])) {
  85041. if (is_array($pinfo['dp'])) {
  85042. $deprecated = array('channel' => (string) $pinfo['dc'],
  85043. 'package' => trim($pinfo['dp']['_content']));
  85044. } else {
  85045. $deprecated = array('channel' => (string) $pinfo['dc'],
  85046. 'package' => trim($pinfo['dp']));
  85047. }
  85048. }
  85049. $return = array(
  85050. 'version' => $releaseinfo['v'],
  85051. 'info' => $packagexml,
  85052. 'package' => $releaseinfo['p']['_content'],
  85053. 'stability' => $releaseinfo['st'],
  85054. 'compatible' => $compatible,
  85055. 'deprecated' => $deprecated,
  85056. );
  85057. if ($found) {
  85058. $return['url'] = $releaseinfo['g'];
  85059. return $return;
  85060. }
  85061. $return['php'] = $phpversion;
  85062. return $return;
  85063. }
  85064. function listPackages($base, $channel = false)
  85065. {
  85066. $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
  85067. if (PEAR::isError($packagelist)) {
  85068. return $packagelist;
  85069. }
  85070. if (!is_array($packagelist) || !isset($packagelist['p'])) {
  85071. return array();
  85072. }
  85073. if (!is_array($packagelist['p'])) {
  85074. $packagelist['p'] = array($packagelist['p']);
  85075. }
  85076. return $packagelist['p'];
  85077. }
  85078. /**
  85079. * List all categories of a REST server
  85080. *
  85081. * @param string $base base URL of the server
  85082. * @return array of categorynames
  85083. */
  85084. function listCategories($base, $channel = false)
  85085. {
  85086. $categories = array();
  85087. // c/categories.xml does not exist;
  85088. // check for every package its category manually
  85089. // This is SLOOOWWWW : ///
  85090. $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
  85091. if (PEAR::isError($packagelist)) {
  85092. return $packagelist;
  85093. }
  85094. if (!is_array($packagelist) || !isset($packagelist['p'])) {
  85095. $ret = array();
  85096. return $ret;
  85097. }
  85098. if (!is_array($packagelist['p'])) {
  85099. $packagelist['p'] = array($packagelist['p']);
  85100. }
  85101. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  85102. foreach ($packagelist['p'] as $package) {
  85103. $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
  85104. if (PEAR::isError($inf)) {
  85105. PEAR::popErrorHandling();
  85106. return $inf;
  85107. }
  85108. $cat = $inf['ca']['_content'];
  85109. if (!isset($categories[$cat])) {
  85110. $categories[$cat] = $inf['ca'];
  85111. }
  85112. }
  85113. return array_values($categories);
  85114. }
  85115. /**
  85116. * List a category of a REST server
  85117. *
  85118. * @param string $base base URL of the server
  85119. * @param string $category name of the category
  85120. * @param boolean $info also download full package info
  85121. * @return array of packagenames
  85122. */
  85123. function listCategory($base, $category, $info = false, $channel = false)
  85124. {
  85125. // gives '404 Not Found' error when category doesn't exist
  85126. $packagelist = $this->_rest->retrieveData($base.'c/'.urlencode($category).'/packages.xml', false, false, $channel);
  85127. if (PEAR::isError($packagelist)) {
  85128. return $packagelist;
  85129. }
  85130. if (!is_array($packagelist) || !isset($packagelist['p'])) {
  85131. return array();
  85132. }
  85133. if (!is_array($packagelist['p']) ||
  85134. !isset($packagelist['p'][0])) { // only 1 pkg
  85135. $packagelist = array($packagelist['p']);
  85136. } else {
  85137. $packagelist = $packagelist['p'];
  85138. }
  85139. if ($info == true) {
  85140. // get individual package info
  85141. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  85142. foreach ($packagelist as $i => $packageitem) {
  85143. $url = sprintf('%s'.'r/%s/latest.txt',
  85144. $base,
  85145. strtolower($packageitem['_content']));
  85146. $version = $this->_rest->retrieveData($url, false, false, $channel);
  85147. if (PEAR::isError($version)) {
  85148. break; // skipit
  85149. }
  85150. $url = sprintf('%s'.'r/%s/%s.xml',
  85151. $base,
  85152. strtolower($packageitem['_content']),
  85153. $version);
  85154. $info = $this->_rest->retrieveData($url, false, false, $channel);
  85155. if (PEAR::isError($info)) {
  85156. break; // skipit
  85157. }
  85158. $packagelist[$i]['info'] = $info;
  85159. }
  85160. PEAR::popErrorHandling();
  85161. }
  85162. return $packagelist;
  85163. }
  85164. function listAll($base, $dostable, $basic = true, $searchpackage = false, $searchsummary = false, $channel = false)
  85165. {
  85166. $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
  85167. if (PEAR::isError($packagelist)) {
  85168. return $packagelist;
  85169. }
  85170. if ($this->_rest->config->get('verbose') > 0) {
  85171. $ui = &PEAR_Frontend::singleton();
  85172. $ui->log('Retrieving data...0%', true);
  85173. }
  85174. $ret = array();
  85175. if (!is_array($packagelist) || !isset($packagelist['p'])) {
  85176. return $ret;
  85177. }
  85178. if (!is_array($packagelist['p'])) {
  85179. $packagelist['p'] = array($packagelist['p']);
  85180. }
  85181. // only search-packagename = quicksearch !
  85182. if ($searchpackage && (!$searchsummary || empty($searchpackage))) {
  85183. $newpackagelist = array();
  85184. foreach ($packagelist['p'] as $package) {
  85185. if (!empty($searchpackage) && stristr($package, $searchpackage) !== false) {
  85186. $newpackagelist[] = $package;
  85187. }
  85188. }
  85189. $packagelist['p'] = $newpackagelist;
  85190. }
  85191. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  85192. $next = .1;
  85193. foreach ($packagelist['p'] as $progress => $package) {
  85194. if ($this->_rest->config->get('verbose') > 0) {
  85195. if ($progress / count($packagelist['p']) >= $next) {
  85196. if ($next == .5) {
  85197. $ui->log('50%', false);
  85198. } else {
  85199. $ui->log('.', false);
  85200. }
  85201. $next += .1;
  85202. }
  85203. }
  85204. if ($basic) { // remote-list command
  85205. if ($dostable) {
  85206. $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
  85207. '/stable.txt', false, false, $channel);
  85208. } else {
  85209. $latest = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
  85210. '/latest.txt', false, false, $channel);
  85211. }
  85212. if (PEAR::isError($latest)) {
  85213. $latest = false;
  85214. }
  85215. $info = array('stable' => $latest);
  85216. } else { // list-all command
  85217. $inf = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
  85218. if (PEAR::isError($inf)) {
  85219. PEAR::popErrorHandling();
  85220. return $inf;
  85221. }
  85222. if ($searchpackage) {
  85223. $found = (!empty($searchpackage) && stristr($package, $searchpackage) !== false);
  85224. if (!$found && !(isset($searchsummary) && !empty($searchsummary)
  85225. && (stristr($inf['s'], $searchsummary) !== false
  85226. || stristr($inf['d'], $searchsummary) !== false)))
  85227. {
  85228. continue;
  85229. };
  85230. }
  85231. $releases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
  85232. '/allreleases.xml', false, false, $channel);
  85233. if (PEAR::isError($releases)) {
  85234. continue;
  85235. }
  85236. if (!isset($releases['r'][0])) {
  85237. $releases['r'] = array($releases['r']);
  85238. }
  85239. unset($latest);
  85240. unset($unstable);
  85241. unset($stable);
  85242. unset($state);
  85243. foreach ($releases['r'] as $release) {
  85244. if (!isset($latest)) {
  85245. if ($dostable && $release['s'] == 'stable') {
  85246. $latest = $release['v'];
  85247. $state = 'stable';
  85248. }
  85249. if (!$dostable) {
  85250. $latest = $release['v'];
  85251. $state = $release['s'];
  85252. }
  85253. }
  85254. if (!isset($stable) && $release['s'] == 'stable') {
  85255. $stable = $release['v'];
  85256. if (!isset($unstable)) {
  85257. $unstable = $stable;
  85258. }
  85259. }
  85260. if (!isset($unstable) && $release['s'] != 'stable') {
  85261. $latest = $unstable = $release['v'];
  85262. $state = $release['s'];
  85263. }
  85264. if (isset($latest) && !isset($state)) {
  85265. $state = $release['s'];
  85266. }
  85267. if (isset($latest) && isset($stable) && isset($unstable)) {
  85268. break;
  85269. }
  85270. }
  85271. $deps = array();
  85272. if (!isset($unstable)) {
  85273. $unstable = false;
  85274. $state = 'stable';
  85275. if (isset($stable)) {
  85276. $latest = $unstable = $stable;
  85277. }
  85278. } else {
  85279. $latest = $unstable;
  85280. }
  85281. if (!isset($latest)) {
  85282. $latest = false;
  85283. }
  85284. if ($latest) {
  85285. $d = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' .
  85286. $latest . '.txt', false, false, $channel);
  85287. if (!PEAR::isError($d)) {
  85288. $d = unserialize($d);
  85289. if ($d) {
  85290. if (isset($d['required'])) {
  85291. if (!class_exists('PEAR_PackageFile_v2')) {
  85292. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/v2.php';
  85293. }
  85294. if (!isset($pf)) {
  85295. $pf = new PEAR_PackageFile_v2;
  85296. }
  85297. $pf->setDeps($d);
  85298. $tdeps = $pf->getDeps();
  85299. } else {
  85300. $tdeps = $d;
  85301. }
  85302. foreach ($tdeps as $dep) {
  85303. if ($dep['type'] !== 'pkg') {
  85304. continue;
  85305. }
  85306. $deps[] = $dep;
  85307. }
  85308. }
  85309. }
  85310. }
  85311. if (!isset($stable)) {
  85312. $stable = '-n/a-';
  85313. }
  85314. if (!$searchpackage) {
  85315. $info = array('stable' => $latest, 'summary' => $inf['s'], 'description' =>
  85316. $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'],
  85317. 'unstable' => $unstable, 'state' => $state);
  85318. } else {
  85319. $info = array('stable' => $stable, 'summary' => $inf['s'], 'description' =>
  85320. $inf['d'], 'deps' => $deps, 'category' => $inf['ca']['_content'],
  85321. 'unstable' => $unstable, 'state' => $state);
  85322. }
  85323. }
  85324. $ret[$package] = $info;
  85325. }
  85326. PEAR::popErrorHandling();
  85327. return $ret;
  85328. }
  85329. function listLatestUpgrades($base, $pref_state, $installed, $channel, &$reg)
  85330. {
  85331. $packagelist = $this->_rest->retrieveData($base . 'p/packages.xml', false, false, $channel);
  85332. if (PEAR::isError($packagelist)) {
  85333. return $packagelist;
  85334. }
  85335. $ret = array();
  85336. if (!is_array($packagelist) || !isset($packagelist['p'])) {
  85337. return $ret;
  85338. }
  85339. if (!is_array($packagelist['p'])) {
  85340. $packagelist['p'] = array($packagelist['p']);
  85341. }
  85342. foreach ($packagelist['p'] as $package) {
  85343. if (!isset($installed[strtolower($package)])) {
  85344. continue;
  85345. }
  85346. $inst_version = $reg->packageInfo($package, 'version', $channel);
  85347. $inst_state = $reg->packageInfo($package, 'release_state', $channel);
  85348. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  85349. $info = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
  85350. '/allreleases.xml', false, false, $channel);
  85351. PEAR::popErrorHandling();
  85352. if (PEAR::isError($info)) {
  85353. continue; // no remote releases
  85354. }
  85355. if (!isset($info['r'])) {
  85356. continue;
  85357. }
  85358. $release = $found = false;
  85359. if (!is_array($info['r']) || !isset($info['r'][0])) {
  85360. $info['r'] = array($info['r']);
  85361. }
  85362. // $info['r'] is sorted by version number
  85363. usort($info['r'], array($this, '_sortReleasesByVersionNumber'));
  85364. foreach ($info['r'] as $release) {
  85365. if ($inst_version && version_compare($release['v'], $inst_version, '<=')) {
  85366. // not newer than the one installed
  85367. break;
  85368. }
  85369. // new version > installed version
  85370. if (!$pref_state) {
  85371. // every state is a good state
  85372. $found = true;
  85373. break;
  85374. } else {
  85375. $new_state = $release['s'];
  85376. // if new state >= installed state: go
  85377. if (in_array($new_state, $this->betterStates($inst_state, true))) {
  85378. $found = true;
  85379. break;
  85380. } else {
  85381. // only allow to lower the state of package,
  85382. // if new state >= preferred state: go
  85383. if (in_array($new_state, $this->betterStates($pref_state, true))) {
  85384. $found = true;
  85385. break;
  85386. }
  85387. }
  85388. }
  85389. }
  85390. if (!$found) {
  85391. continue;
  85392. }
  85393. $relinfo = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/' .
  85394. $release['v'] . '.xml', false, false, $channel);
  85395. if (PEAR::isError($relinfo)) {
  85396. return $relinfo;
  85397. }
  85398. $ret[$package] = array(
  85399. 'version' => $release['v'],
  85400. 'state' => $release['s'],
  85401. 'filesize' => $relinfo['f'],
  85402. );
  85403. }
  85404. return $ret;
  85405. }
  85406. function packageInfo($base, $package, $channel = false)
  85407. {
  85408. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  85409. $pinfo = $this->_rest->retrieveData($base . 'p/' . strtolower($package) . '/info.xml', false, false, $channel);
  85410. if (PEAR::isError($pinfo)) {
  85411. PEAR::popErrorHandling();
  85412. return PEAR::raiseError('Unknown package: "' . $package . '" in channel "' . $channel . '"' . "\n". 'Debug: ' .
  85413. $pinfo->getMessage());
  85414. }
  85415. $releases = array();
  85416. $allreleases = $this->_rest->retrieveData($base . 'r/' . strtolower($package) .
  85417. '/allreleases.xml', false, false, $channel);
  85418. if (!PEAR::isError($allreleases)) {
  85419. if (!class_exists('PEAR_PackageFile_v2')) {
  85420. require_once 'phar://go-pear.phar/' . 'PEAR/PackageFile/v2.php';
  85421. }
  85422. if (!is_array($allreleases['r']) || !isset($allreleases['r'][0])) {
  85423. $allreleases['r'] = array($allreleases['r']);
  85424. }
  85425. $pf = new PEAR_PackageFile_v2;
  85426. foreach ($allreleases['r'] as $release) {
  85427. $ds = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package) . '/deps.' .
  85428. $release['v'] . '.txt', false, false, $channel);
  85429. if (PEAR::isError($ds)) {
  85430. continue;
  85431. }
  85432. if (!isset($latest)) {
  85433. $latest = $release['v'];
  85434. }
  85435. $pf->setDeps(unserialize($ds));
  85436. $ds = $pf->getDeps();
  85437. $info = $this->_rest->retrieveCacheFirst($base . 'r/' . strtolower($package)
  85438. . '/' . $release['v'] . '.xml', false, false, $channel);
  85439. if (PEAR::isError($info)) {
  85440. continue;
  85441. }
  85442. $releases[$release['v']] = array(
  85443. 'doneby' => $info['m'],
  85444. 'license' => $info['l'],
  85445. 'summary' => $info['s'],
  85446. 'description' => $info['d'],
  85447. 'releasedate' => $info['da'],
  85448. 'releasenotes' => $info['n'],
  85449. 'state' => $release['s'],
  85450. 'deps' => $ds ? $ds : array(),
  85451. );
  85452. }
  85453. } else {
  85454. $latest = '';
  85455. }
  85456. PEAR::popErrorHandling();
  85457. if (isset($pinfo['dc']) && isset($pinfo['dp'])) {
  85458. if (is_array($pinfo['dp'])) {
  85459. $deprecated = array('channel' => (string) $pinfo['dc'],
  85460. 'package' => trim($pinfo['dp']['_content']));
  85461. } else {
  85462. $deprecated = array('channel' => (string) $pinfo['dc'],
  85463. 'package' => trim($pinfo['dp']));
  85464. }
  85465. } else {
  85466. $deprecated = false;
  85467. }
  85468. if (!isset($latest)) {
  85469. $latest = '';
  85470. }
  85471. return array(
  85472. 'name' => $pinfo['n'],
  85473. 'channel' => $pinfo['c'],
  85474. 'category' => $pinfo['ca']['_content'],
  85475. 'stable' => $latest,
  85476. 'license' => $pinfo['l'],
  85477. 'summary' => $pinfo['s'],
  85478. 'description' => $pinfo['d'],
  85479. 'releases' => $releases,
  85480. 'deprecated' => $deprecated,
  85481. );
  85482. }
  85483. /**
  85484. * Return an array containing all of the states that are more stable than
  85485. * or equal to the passed in state
  85486. *
  85487. * @param string Release state
  85488. * @param boolean Determines whether to include $state in the list
  85489. * @return false|array False if $state is not a valid release state
  85490. */
  85491. function betterStates($state, $include = false)
  85492. {
  85493. static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
  85494. $i = array_search($state, $states);
  85495. if ($i === false) {
  85496. return false;
  85497. }
  85498. if ($include) {
  85499. $i--;
  85500. }
  85501. return array_slice($states, $i + 1);
  85502. }
  85503. /**
  85504. * Sort releases by version number
  85505. *
  85506. * @access private
  85507. */
  85508. function _sortReleasesByVersionNumber($a, $b)
  85509. {
  85510. if (version_compare($a['v'], $b['v'], '=')) {
  85511. return 0;
  85512. }
  85513. if (version_compare($a['v'], $b['v'], '>')) {
  85514. return -1;
  85515. }
  85516. if (version_compare($a['v'], $b['v'], '<')) {
  85517. return 1;
  85518. }
  85519. }
  85520. }
  85521. <?php
  85522. require_once 'phar://go-pear.phar/' . 'PEAR.php';
  85523. require_once 'phar://go-pear.phar/' . 'System.php';
  85524. require_once 'phar://go-pear.phar/' . 'PEAR/Config.php';
  85525. require_once 'phar://go-pear.phar/' . 'PEAR/Command.php';
  85526. require_once 'phar://go-pear.phar/' . 'PEAR/Common.php';
  85527. class PEAR_Start extends PEAR
  85528. {
  85529. var $bin_dir;
  85530. var $data_dir;
  85531. var $cfg_dir;
  85532. var $www_dir;
  85533. var $man_dir;
  85534. var $install_pfc;
  85535. var $corePackages =
  85536. array(
  85537. 'Archive_Tar',
  85538. 'Console_Getopt',
  85539. 'PEAR',
  85540. 'Structures_Graph',
  85541. 'XML_Util',
  85542. );
  85543. var $local_dir = array();
  85544. var $origpwd;
  85545. var $pfc_packages = array(
  85546. 'DB',
  85547. 'Net_Socket',
  85548. 'Net_SMTP',
  85549. 'Mail',
  85550. 'XML_Parser',
  85551. 'XML_RPC',
  85552. 'PHPUnit'
  85553. );
  85554. var $php_dir;
  85555. var $php_bin;
  85556. var $pear_conf;
  85557. var $validPHPBin = false;
  85558. var $test_dir;
  85559. var $download_dir;
  85560. var $temp_dir;
  85561. var $config =
  85562. array(
  85563. 'prefix',
  85564. 'bin_dir',
  85565. 'php_dir',
  85566. 'doc_dir',
  85567. 'data_dir',
  85568. 'cfg_dir',
  85569. 'www_dir',
  85570. 'man_dir',
  85571. 'test_dir',
  85572. 'temp_dir',
  85573. 'download_dir',
  85574. 'pear_conf',
  85575. );
  85576. var $prefix;
  85577. var $progress = 0;
  85578. var $configPrompt =
  85579. array(
  85580. 'prefix' => 'Installation base ($prefix)',
  85581. 'temp_dir' => 'Temporary directory for processing',
  85582. 'download_dir' => 'Temporary directory for downloads',
  85583. 'bin_dir' => 'Binaries directory',
  85584. 'php_dir' => 'PHP code directory ($php_dir)',
  85585. 'doc_dir' => 'Documentation directory',
  85586. 'data_dir' => 'Data directory',
  85587. 'cfg_dir' => 'User-modifiable configuration files directory',
  85588. 'www_dir' => 'Public Web Files directory',
  85589. 'man_dir' => 'System manual pages directory',
  85590. 'test_dir' => 'Tests directory',
  85591. 'pear_conf' => 'Name of configuration file',
  85592. );
  85593. var $localInstall;
  85594. var $PEARConfig;
  85595. var $tarball = array();
  85596. var $ptmp;
  85597. function __construct()
  85598. {
  85599. parent::__construct();
  85600. if (OS_WINDOWS) {
  85601. $this->configPrompt['php_bin'] = 'Path to CLI php.exe';
  85602. $this->config[] = 'php_bin';
  85603. $this->prefix = getcwd();
  85604. if (!@is_dir($this->prefix)) {
  85605. if (@is_dir('c:\php5')) {
  85606. $this->prefix = 'c:\php5';
  85607. } elseif (@is_dir('c:\php4')) {
  85608. $this->prefix = 'c:\php4';
  85609. } elseif (@is_dir('c:\php')) {
  85610. $this->prefix = 'c:\php';
  85611. }
  85612. }
  85613. $slash = "\\";
  85614. if (strrpos($this->prefix, '\\') === (strlen($this->prefix) - 1)) {
  85615. $slash = '';
  85616. }
  85617. $this->localInstall = false;
  85618. $this->bin_dir = '$prefix';
  85619. $this->temp_dir = '$prefix' . $slash . 'tmp';
  85620. $this->download_dir = '$prefix' . $slash . 'tmp';
  85621. $this->php_dir = '$prefix' . $slash . 'pear';
  85622. $this->doc_dir = '$prefix' . $slash . 'docs';
  85623. $this->data_dir = '$prefix' . $slash . 'data';
  85624. $this->test_dir = '$prefix' . $slash . 'tests';
  85625. $this->www_dir = '$prefix' . $slash . 'www';
  85626. $this->man_dir = '$prefix' . $slash . 'man';
  85627. $this->cfg_dir = '$prefix' . $slash . 'cfg';
  85628. $this->pear_conf = PEAR_CONFIG_SYSCONFDIR . '\\pear.ini';
  85629. /*
  85630. * Detects php.exe
  85631. */
  85632. $this->validPHPBin = true;
  85633. if ($t = $this->safeGetenv('PHP_PEAR_PHP_BIN')) {
  85634. $this->php_bin = dirname($t);
  85635. } elseif ($t = $this->safeGetenv('PHP_BIN')) {
  85636. $this->php_bin = dirname($t);
  85637. } elseif ($t = System::which('php')) {
  85638. $this->php_bin = dirname($t);
  85639. } elseif (is_file($this->prefix . '\cli\php.exe')) {
  85640. $this->php_bin = $this->prefix . '\cli';
  85641. } elseif (is_file($this->prefix . '\php.exe')) {
  85642. $this->php_bin = $this->prefix;
  85643. }
  85644. $phpexe = OS_WINDOWS ? '\\php.exe' : '/php';
  85645. if ($this->php_bin && !is_file($this->php_bin . $phpexe)) {
  85646. $this->php_bin = '';
  85647. } else {
  85648. if (strpos($this->php_bin, ':') === 0) {
  85649. $this->php_bin = getcwd() . DIRECTORY_SEPARATOR . $this->php_bin;
  85650. }
  85651. }
  85652. if (!is_file($this->php_bin . $phpexe)) {
  85653. if (is_file('c:/php/cli/php.exe')) {
  85654. $this->php_bin = 'c"\\php\\cli';
  85655. } elseif (is_file('c:/php5/php.exe')) {
  85656. $this->php_bin = 'c:\\php5';
  85657. } elseif (is_file('c:/php4/cli/php.exe')) {
  85658. $this->php_bin = 'c:\\php4\\cli';
  85659. } else {
  85660. $this->validPHPBin = false;
  85661. }
  85662. }
  85663. } else {
  85664. $this->prefix = dirname(PHP_BINDIR);
  85665. $this->pear_conf = PEAR_CONFIG_SYSCONFDIR . '/pear.conf';
  85666. if ($this->getCurrentUser() != 'root') {
  85667. $this->prefix = $this->safeGetenv('HOME') . '/pear';
  85668. $this->pear_conf = $this->safeGetenv('HOME') . '.pearrc';
  85669. }
  85670. $this->bin_dir = '$prefix/bin';
  85671. $this->php_dir = '$prefix/share/pear';
  85672. $this->temp_dir = '/tmp/pear/install';
  85673. $this->download_dir = '/tmp/pear/install';
  85674. $this->doc_dir = '$prefix/docs';
  85675. $this->www_dir = '$prefix/www';
  85676. $this->cfg_dir = '$prefix/cfg';
  85677. $this->data_dir = '$prefix/data';
  85678. $this->test_dir = '$prefix/tests';
  85679. $this->man_dir = '$prefix/man';
  85680. // check if the user has installed PHP with PHP or GNU layout
  85681. if (@is_dir("$this->prefix/lib/php/.registry")) {
  85682. $this->php_dir = '$prefix/lib/php';
  85683. } elseif (@is_dir("$this->prefix/share/pear/lib/.registry")) {
  85684. $this->php_dir = '$prefix/share/pear/lib';
  85685. $this->doc_dir = '$prefix/share/pear/docs';
  85686. $this->data_dir = '$prefix/share/pear/data';
  85687. $this->test_dir = '$prefix/share/pear/tests';
  85688. } elseif (@is_dir("$this->prefix/share/php/.registry")) {
  85689. $this->php_dir = '$prefix/share/php';
  85690. }
  85691. }
  85692. }
  85693. /**
  85694. * Get the name of the user running the script.
  85695. * Only needed on unix for now.
  85696. *
  85697. * @return string Name of the user ("root", "cweiske")
  85698. */
  85699. function getCurrentUser()
  85700. {
  85701. if (isset($_ENV['USER'])) {
  85702. return $_ENV['USER'];
  85703. } else {
  85704. return trim(`whoami`);
  85705. }
  85706. }
  85707. function safeGetenv($var)
  85708. {
  85709. if (is_array($_ENV) && isset($_ENV[$var])) {
  85710. return $_ENV[$var];
  85711. }
  85712. return getenv($var);
  85713. }
  85714. function show($stuff)
  85715. {
  85716. print $stuff;
  85717. }
  85718. function locatePackagesToInstall()
  85719. {
  85720. $dp = @opendir(dirname(__FILE__) . '/go-pear-tarballs');
  85721. if (empty($dp)) {
  85722. return PEAR::raiseError("while locating packages to install: opendir('" .
  85723. dirname(__FILE__) . "/go-pear-tarballs') failed");
  85724. }
  85725. $potentials = array();
  85726. while (false !== ($entry = readdir($dp))) {
  85727. if ($entry[0] == '.' || !in_array(substr($entry, -4), array('.tar', '.tgz'))) {
  85728. continue;
  85729. }
  85730. $potentials[] = $entry;
  85731. }
  85732. closedir($dp);
  85733. $notfound = array();
  85734. foreach ($this->corePackages as $package) {
  85735. foreach ($potentials as $i => $candidate) {
  85736. if (preg_match('/^' . $package . '-' . _PEAR_COMMON_PACKAGE_VERSION_PREG
  85737. . '\.(tar|tgz)\\z/', $candidate)) {
  85738. $this->tarball[$package] = dirname(__FILE__) . '/go-pear-tarballs/' . $candidate;
  85739. unset($potentials[$i]);
  85740. continue 2;
  85741. }
  85742. }
  85743. $notfound[] = $package;
  85744. }
  85745. if (count($notfound)) {
  85746. return PEAR::raiseError("No tarballs found for core packages: " .
  85747. implode(', ', $notfound));
  85748. }
  85749. $this->tarball = array_merge($this->tarball, $potentials);
  85750. }
  85751. function setupTempStuff()
  85752. {
  85753. if (!($this->ptmp = System::mktemp(array('-d')))) {
  85754. $this->show("System's Tempdir failed, trying to use \$prefix/tmp ...");
  85755. $res = System::mkDir(array($this->prefix . '/tmp'));
  85756. if (!$res) {
  85757. return PEAR::raiseError('mkdir ' . $this->prefix . '/tmp ... failed');
  85758. }
  85759. $_temp = tempnam($this->prefix . '/tmp', 'gope');
  85760. System::rm(array('-rf', $_temp));
  85761. System::mkdir(array('-p','-m', '0700', $_temp));
  85762. $this->ptmp = $this->prefix . '/tmp';
  85763. $ok = @chdir($this->ptmp);
  85764. if (!$ok) { // This should not happen, really ;)
  85765. $this->bail('chdir ' . $this->ptmp . ' ... failed');
  85766. }
  85767. print "ok\n";
  85768. // Adjust TEMPDIR envvars
  85769. if (!isset($_ENV)) {
  85770. $_ENV = array();
  85771. };
  85772. $_ENV['TMPDIR'] = $_ENV['TEMP'] = $this->prefix . '/tmp';
  85773. }
  85774. return @chdir($this->ptmp);
  85775. }
  85776. /**
  85777. * Try to detect the kind of SAPI used by the
  85778. * the given php.exe.
  85779. * @author Pierrre-Alain Joye
  85780. */
  85781. function win32DetectPHPSAPI()
  85782. {
  85783. if ($this->php_bin != '') {
  85784. if (OS_WINDOWS) {
  85785. exec('"' . $this->php_bin . '\\php.exe" -v', $res);
  85786. } else {
  85787. exec('"' . $this->php_bin . '/php" -v', $res);
  85788. }
  85789. if (is_array($res)) {
  85790. if (isset($res[0]) && strpos($res[0],"(cli)")) {
  85791. return 'cli';
  85792. }
  85793. if (isset($res[0]) && strpos($res[0],"cgi")) {
  85794. return 'cgi';
  85795. }
  85796. if (isset($res[0]) && strpos($res[0],"cgi-fcgi")) {
  85797. return 'cgi';
  85798. }
  85799. return 'unknown';
  85800. }
  85801. }
  85802. return 'unknown';
  85803. }
  85804. function doInstall()
  85805. {
  85806. print "Beginning install...\n";
  85807. // finish php_bin config
  85808. if (OS_WINDOWS) {
  85809. $this->php_bin .= '\\php.exe';
  85810. } else {
  85811. $this->php_bin .= '/php';
  85812. }
  85813. $this->PEARConfig = &PEAR_Config::singleton($this->pear_conf, $this->pear_conf);
  85814. $this->PEARConfig->set('preferred_state', 'stable');
  85815. foreach ($this->config as $var) {
  85816. if ($var == 'pear_conf' || $var == 'prefix') {
  85817. continue;
  85818. }
  85819. $this->PEARConfig->set($var, $this->$var);
  85820. }
  85821. $this->PEARConfig->store();
  85822. // $this->PEARConfig->set('verbose', 6);
  85823. print "Configuration written to $this->pear_conf...\n";
  85824. $this->registry = &$this->PEARConfig->getRegistry();
  85825. print "Initialized registry...\n";
  85826. $install = &PEAR_Command::factory('install', $this->PEARConfig);
  85827. print "Preparing to install...\n";
  85828. $options = array(
  85829. 'nodeps' => true,
  85830. 'force' => true,
  85831. 'upgrade' => true,
  85832. );
  85833. foreach ($this->tarball as $pkg => $src) {
  85834. print "installing $src...\n";
  85835. }
  85836. $install->run('install', $options, array_values($this->tarball));
  85837. }
  85838. function postProcessConfigVars()
  85839. {
  85840. foreach ($this->config as $n => $var) {
  85841. for ($m = 1; $m <= count($this->config); $m++) {
  85842. $var2 = $this->config[$m];
  85843. $this->$var = str_replace('$'.$var2, $this->$var2, $this->$var);
  85844. }
  85845. }
  85846. foreach ($this->config as $var) {
  85847. $dir = $this->$var;
  85848. if (!preg_match('/_dir\\z/', $var)) {
  85849. continue;
  85850. }
  85851. if (!@is_dir($dir)) {
  85852. if (!System::mkDir(array('-p', $dir))) {
  85853. $root = OS_WINDOWS ? 'administrator' : 'root';
  85854. return PEAR::raiseError("Unable to create {$this->configPrompt[$var]} $dir.
  85855. Run this script as $root or pick another location.\n");
  85856. }
  85857. }
  85858. }
  85859. }
  85860. /**
  85861. * Get the php.ini file used with the current
  85862. * process or with the given php.exe
  85863. *
  85864. * Horrible hack, but well ;)
  85865. *
  85866. * Not used yet, will add the support later
  85867. * @author Pierre-Alain Joye <paj@pearfr.org>
  85868. */
  85869. function getPhpiniPath()
  85870. {
  85871. $pathIni = get_cfg_var('cfg_file_path');
  85872. if ($pathIni && is_file($pathIni)) {
  85873. return $pathIni;
  85874. }
  85875. // Oh well, we can keep this too :)
  85876. // I dunno if get_cfg_var() is safe on every OS
  85877. if (OS_WINDOWS) {
  85878. // on Windows, we can be pretty sure that there is a php.ini
  85879. // file somewhere
  85880. do {
  85881. $php_ini = PHP_CONFIG_FILE_PATH . DIRECTORY_SEPARATOR . 'php.ini';
  85882. if (@file_exists($php_ini)) {
  85883. break;
  85884. }
  85885. $php_ini = 'c:\winnt\php.ini';
  85886. if (@file_exists($php_ini)) {
  85887. break;
  85888. }
  85889. $php_ini = 'c:\windows\php.ini';
  85890. } while (false);
  85891. } else {
  85892. $php_ini = PHP_CONFIG_FILE_PATH . DIRECTORY_SEPARATOR . 'php.ini';
  85893. }
  85894. if (@is_file($php_ini)) {
  85895. return $php_ini;
  85896. }
  85897. // We re running in hackz&troubles :)
  85898. ob_implicit_flush(false);
  85899. ob_start();
  85900. phpinfo(INFO_GENERAL);
  85901. $strInfo = ob_get_contents();
  85902. ob_end_clean();
  85903. ob_implicit_flush(true);
  85904. if (php_sapi_name() != 'cli') {
  85905. $strInfo = strip_tags($strInfo,'<td>');
  85906. $arrayInfo = explode("</td>", $strInfo );
  85907. $cli = false;
  85908. } else {
  85909. $arrayInfo = explode("\n", $strInfo);
  85910. $cli = true;
  85911. }
  85912. foreach ($arrayInfo as $val) {
  85913. if (strpos($val,"php.ini")) {
  85914. if ($cli) {
  85915. list(,$pathIni) = explode('=>', $val);
  85916. } else {
  85917. $pathIni = strip_tags(trim($val));
  85918. }
  85919. $pathIni = trim($pathIni);
  85920. if (is_file($pathIni)) {
  85921. return $pathIni;
  85922. }
  85923. }
  85924. }
  85925. return false;
  85926. }
  85927. }
  85928. ?>
  85929. <?php
  85930. require_once 'phar://go-pear.phar/' . 'PEAR/Start.php';
  85931. class PEAR_Start_CLI extends PEAR_Start
  85932. {
  85933. var $descLength;
  85934. var $descFormat;
  85935. var $first;
  85936. var $last;
  85937. var $origpwd;
  85938. var $tty;
  85939. /**
  85940. * Path to .vbs file for directory selection
  85941. * @var string
  85942. */
  85943. var $cscript;
  85944. /**
  85945. * SAPI for selected php.exe
  85946. * @var string
  85947. */
  85948. var $php_bin_sapi;
  85949. function __construct()
  85950. {
  85951. parent::__construct();
  85952. ini_set('html_errors', 0);
  85953. define('WIN32GUI', OS_WINDOWS && php_sapi_name() == 'cli' && System::which('cscript'));
  85954. $this->tty = OS_WINDOWS ? @fopen('\con', 'r') : @fopen('/dev/tty', 'r');
  85955. if (!$this->tty) {
  85956. $this->tty = fopen('php://stdin', 'r');
  85957. }
  85958. $this->origpwd = getcwd();
  85959. $this->config = array_keys($this->configPrompt);
  85960. // make indices run from 1...
  85961. array_unshift($this->config, "");
  85962. unset($this->config[0]);
  85963. reset($this->config);
  85964. $this->descLength = max(array_map('strlen', $this->configPrompt));
  85965. $this->descFormat = "%-{$this->descLength}s";
  85966. $this->first = key($this->config);
  85967. end($this->config);
  85968. $this->last = key($this->config);
  85969. PEAR_Command::setFrontendType('CLI');
  85970. }
  85971. function _PEAR_Start_CLI()
  85972. {
  85973. if ($this->tty) {
  85974. @fclose($this->tty);
  85975. }
  85976. if ($this->cscript) {
  85977. @unlink($this->cscript);
  85978. }
  85979. }
  85980. function run()
  85981. {
  85982. if (PEAR::isError($err = $this->locatePackagesToInstall())) {
  85983. return $err;
  85984. }
  85985. $this->startupQuestion();
  85986. $this->setupTempStuff();
  85987. $this->getInstallLocations();
  85988. $this->displayPreamble();
  85989. if (PEAR::isError($err = $this->postProcessConfigVars())) {
  85990. return $err;
  85991. }
  85992. $this->doInstall();
  85993. $this->finishInstall();
  85994. }
  85995. function startupQuestion()
  85996. {
  85997. if (OS_WINDOWS) {
  85998. print "
  85999. Are you installing a system-wide PEAR or a local copy?
  86000. (system|local) [system] : ";
  86001. $tmp = trim(fgets($this->tty, 1024));
  86002. if (!empty($tmp) && strtolower($tmp) !== 'system') {
  86003. print "Please confirm local copy by typing 'yes' : ";
  86004. $tmp = trim(fgets($this->tty, 1024));
  86005. if (strtolower($tmp) == 'yes') {
  86006. $slash = "\\";
  86007. if (strrpos($this->prefix, '\\') === (strlen($this->prefix) - 1)) {
  86008. $slash = '';
  86009. }
  86010. $this->localInstall = true;
  86011. $this->pear_conf = '$prefix' . $slash . 'pear.ini';
  86012. }
  86013. }
  86014. } else {
  86015. if ($this->getCurrentUser() == 'root') {
  86016. return;
  86017. }
  86018. $this->pear_conf = $this->safeGetenv('HOME') . '/.pearrc';
  86019. }
  86020. }
  86021. function getInstallLocations()
  86022. {
  86023. while (true) {
  86024. print "
  86025. Below is a suggested file layout for your new PEAR installation. To
  86026. change individual locations, type the number in front of the
  86027. directory. Type 'all' to change all of them or simply press Enter to
  86028. accept these locations.
  86029. ";
  86030. foreach ($this->config as $n => $var) {
  86031. $fullvar = $this->$var;
  86032. foreach ($this->config as $blah => $unused) {
  86033. foreach ($this->config as $m => $var2) {
  86034. $fullvar = str_replace('$'.$var2, $this->$var2, $fullvar);
  86035. }
  86036. }
  86037. printf("%2d. $this->descFormat : %s\n", $n, $this->configPrompt[$var], $fullvar);
  86038. }
  86039. print "\n$this->first-$this->last, 'all' or Enter to continue: ";
  86040. $tmp = trim(fgets($this->tty, 1024));
  86041. if (empty($tmp)) {
  86042. if (OS_WINDOWS && !$this->validPHPBin) {
  86043. echo "**ERROR**
  86044. Please, enter the php.exe path.
  86045. ";
  86046. } else {
  86047. break;
  86048. }
  86049. }
  86050. if (isset($this->config[(int)$tmp])) {
  86051. $var = $this->config[(int)$tmp];
  86052. $desc = $this->configPrompt[$var];
  86053. $current = $this->$var;
  86054. if (WIN32GUI && $var != 'pear_conf'){
  86055. $tmp = $this->win32BrowseForFolder("Choose a Folder for $desc [$current] :");
  86056. $tmp.= '\\';
  86057. } else {
  86058. print "(Use \$prefix as a shortcut for '$this->prefix', etc.)
  86059. $desc [$current] : ";
  86060. $tmp = trim(fgets($this->tty, 1024));
  86061. }
  86062. $old = $this->$var;
  86063. $this->$var = $$var = $tmp;
  86064. if (OS_WINDOWS && $var=='php_bin') {
  86065. if ($this->validatePhpExecutable($tmp)) {
  86066. $this->php_bin = $tmp;
  86067. } else {
  86068. $this->php_bin = $old;
  86069. }
  86070. }
  86071. } elseif ($tmp == 'all') {
  86072. foreach ($this->config as $n => $var) {
  86073. $desc = $this->configPrompt[$var];
  86074. $current = $this->$var;
  86075. print "$desc [$current] : ";
  86076. $tmp = trim(fgets($this->tty, 1024));
  86077. if (!empty($tmp)) {
  86078. $this->$var = $tmp;
  86079. }
  86080. }
  86081. }
  86082. }
  86083. }
  86084. function validatePhpExecutable($tmp)
  86085. {
  86086. if (OS_WINDOWS) {
  86087. if (strpos($tmp, 'php.exe')) {
  86088. $tmp = str_replace('php.exe', '', $tmp);
  86089. }
  86090. if (file_exists($tmp . DIRECTORY_SEPARATOR . 'php.exe')) {
  86091. $tmp = $tmp . DIRECTORY_SEPARATOR . 'php.exe';
  86092. $this->php_bin_sapi = $this->win32DetectPHPSAPI();
  86093. if ($this->php_bin_sapi=='cgi'){
  86094. print "
  86095. ******************************************************************************
  86096. NOTICE! We found php.exe under $this->php_bin, it uses a $this->php_bin_sapi SAPI.
  86097. PEAR commandline tool works well with it.
  86098. If you have a CLI php.exe available, we recommend using it.
  86099. Press Enter to continue...";
  86100. $tmp = trim(fgets($this->tty, 1024));
  86101. } elseif ($this->php_bin_sapi=='unknown') {
  86102. print "
  86103. ******************************************************************************
  86104. WARNING! We found php.exe under $this->php_bin, it uses an $this->php_bin_sapi SAPI.
  86105. PEAR commandline tool has NOT been tested with it.
  86106. If you have a CLI (or CGI) php.exe available, we strongly recommend using it.
  86107. Press Enter to continue...";
  86108. $tmp = trim(fgets($this->tty, 1024));
  86109. }
  86110. echo "php.exe (sapi: $this->php_bin_sapi) found.\n\n";
  86111. return $this->validPHPBin = true;
  86112. } else {
  86113. echo "**ERROR**: not a folder, or no php.exe found in this folder.
  86114. Press Enter to continue...";
  86115. $tmp = trim(fgets($this->tty, 1024));
  86116. return $this->validPHPBin = false;
  86117. }
  86118. }
  86119. }
  86120. /**
  86121. * Create a vbs script to browse the getfolder dialog, called
  86122. * by cscript, if it's available.
  86123. * $label is the label text in the header of the dialog box
  86124. *
  86125. * TODO:
  86126. * - Do not show Control panel
  86127. * - Replace WSH with calls to w32 as soon as callbacks work
  86128. * @author Pierrre-Alain Joye
  86129. */
  86130. function win32BrowseForFolder($label)
  86131. {
  86132. $wsh_browserfolder = 'Option Explicit
  86133. Dim ArgObj, var1, var2, sa, sFld
  86134. Set ArgObj = WScript.Arguments
  86135. Const BIF_EDITBOX = &H10
  86136. Const BIF_NEWDIALOGSTYLE = &H40
  86137. Const BIF_RETURNONLYFSDIRS = &H0001
  86138. Const BIF_DONTGOBELOWDOMAIN = &H0002
  86139. Const BIF_STATUSTEXT = &H0004
  86140. Const BIF_RETURNFSANCESTORS = &H0008
  86141. Const BIF_VALIDATE = &H0020
  86142. Const BIF_BROWSEFORCOMPUTER = &H1000
  86143. Const BIF_BROWSEFORPRINTER = &H2000
  86144. Const BIF_BROWSEINCLUDEFILES = &H4000
  86145. Const OFN_LONGNAMES = &H200000
  86146. Const OFN_NOLONGNAMES = &H40000
  86147. Const ssfDRIVES = &H11
  86148. Const ssfNETWORK = &H12
  86149. Set sa = CreateObject("Shell.Application")
  86150. var1=ArgObj(0)
  86151. Set sFld = sa.BrowseForFolder(0, var1, BIF_EDITBOX + BIF_VALIDATE + BIF_BROWSEINCLUDEFILES + BIF_RETURNFSANCESTORS+BIF_NEWDIALOGSTYLE , ssfDRIVES )
  86152. if not sFld is nothing Then
  86153. if not left(sFld.items.item.path,1)=":" Then
  86154. WScript.Echo sFld.items.item.path
  86155. Else
  86156. WScript.Echo "invalid"
  86157. End If
  86158. Else
  86159. WScript.Echo "cancel"
  86160. End If
  86161. ';
  86162. if (!$this->cscript) {
  86163. $this->cscript = $this->ptmp . DIRECTORY_SEPARATOR . "bf.vbs";
  86164. // TODO: use file_put_contents()
  86165. $fh = fopen($this->cscript, "wb+");
  86166. fwrite($fh, $wsh_browserfolder, strlen($wsh_browserfolder));
  86167. fclose($fh);
  86168. }
  86169. exec('cscript ' . escapeshellarg($this->cscript) . ' "' . escapeshellarg($label) . '" //noLogo', $arPath);
  86170. if (!count($arPath) || $arPath[0]=='' || $arPath[0]=='cancel') {
  86171. return '';
  86172. } elseif ($arPath[0]=='invalid') {
  86173. echo "Invalid Path.\n";
  86174. return '';
  86175. }
  86176. return $arPath[0];
  86177. }
  86178. function displayPreamble()
  86179. {
  86180. if (OS_WINDOWS) {
  86181. /*
  86182. * Checks PHP SAPI version under windows/CLI
  86183. */
  86184. if ($this->php_bin == '') {
  86185. print "
  86186. We do not find any php.exe, please select the php.exe folder (CLI is
  86187. recommended, usually in c:\php\cli\php.exe)
  86188. ";
  86189. $this->validPHPBin = false;
  86190. } elseif (strlen($this->php_bin)) {
  86191. $this->php_bin_sapi = $this->win32DetectPHPSAPI();
  86192. $this->validPHPBin = true;
  86193. switch ($this->php_bin_sapi) {
  86194. case 'cli':
  86195. break;
  86196. case 'cgi':
  86197. case 'cgi-fcgi':
  86198. print "
  86199. *NOTICE*
  86200. We found php.exe under $this->php_bin, it uses a $this->php_bin_sapi SAPI. PEAR commandline
  86201. tool works well with it, if you have a CLI php.exe available, we
  86202. recommend using it.
  86203. ";
  86204. break;
  86205. default:
  86206. print "
  86207. *WARNING*
  86208. We found php.exe under $this->php_bin, it uses an unknown SAPI. PEAR commandline
  86209. tool has not been tested with it, if you have a CLI (or CGI) php.exe available,
  86210. we strongly recommend using it.
  86211. ";
  86212. break;
  86213. }
  86214. }
  86215. }
  86216. }
  86217. function finishInstall()
  86218. {
  86219. $sep = OS_WINDOWS ? ';' : ':';
  86220. $include_path = explode($sep, ini_get('include_path'));
  86221. if (OS_WINDOWS) {
  86222. $found = false;
  86223. $t = strtolower($this->php_dir);
  86224. foreach ($include_path as $path) {
  86225. if ($t == strtolower($path)) {
  86226. $found = true;
  86227. break;
  86228. }
  86229. }
  86230. } else {
  86231. $found = in_array($this->php_dir, $include_path);
  86232. }
  86233. if (!$found) {
  86234. print "
  86235. ******************************************************************************
  86236. WARNING! The include_path defined in the currently used php.ini does not
  86237. contain the PEAR PHP directory you just specified:
  86238. <$this->php_dir>
  86239. If the specified directory is also not in the include_path used by
  86240. your scripts, you will have problems getting any PEAR packages working.
  86241. ";
  86242. if ($php_ini = $this->getPhpiniPath()) {
  86243. print "\n\nWould you like to alter php.ini <$php_ini>? [Y/n] : ";
  86244. $alter_phpini = !stristr(fgets($this->tty, 1024), "n");
  86245. if ($alter_phpini) {
  86246. $this->alterPhpIni($php_ini);
  86247. } else {
  86248. if (OS_WINDOWS) {
  86249. print "
  86250. Please look over your php.ini file to make sure
  86251. $this->php_dir is in your include_path.";
  86252. } else {
  86253. print "
  86254. I will add a workaround for this in the 'pear' command to make sure
  86255. the installer works, but please look over your php.ini or Apache
  86256. configuration to make sure $this->php_dir is in your include_path.
  86257. ";
  86258. }
  86259. }
  86260. }
  86261. print "
  86262. Current include path : ".ini_get('include_path')."
  86263. Configured directory : $this->php_dir
  86264. Currently used php.ini (guess) : $php_ini
  86265. ";
  86266. print "Press Enter to continue: ";
  86267. fgets($this->tty, 1024);
  86268. }
  86269. $pear_cmd = $this->bin_dir . DIRECTORY_SEPARATOR . 'pear';
  86270. $pear_cmd = OS_WINDOWS ? strtolower($pear_cmd).'.bat' : $pear_cmd;
  86271. // check that the installed pear and the one in the path are the same (if any)
  86272. $pear_old = System::which(OS_WINDOWS ? 'pear.bat' : 'pear', $this->bin_dir);
  86273. if ($pear_old && ($pear_old != $pear_cmd)) {
  86274. // check if it is a link or symlink
  86275. $islink = OS_WINDOWS ? false : is_link($pear_old) ;
  86276. if ($islink && readlink($pear_old) != $pear_cmd) {
  86277. print "\n** WARNING! The link $pear_old does not point to the " .
  86278. "installed $pear_cmd\n";
  86279. } elseif (!$this->localInstall && is_writable($pear_old) && !is_dir($pear_old)) {
  86280. rename($pear_old, "{$pear_old}_old");
  86281. print "\n** WARNING! Backed up old pear to {$pear_old}_old\n";
  86282. } else {
  86283. print "\n** WARNING! Old version found at $pear_old, please remove it or ".
  86284. "be sure to use the new $pear_cmd command\n";
  86285. }
  86286. }
  86287. print "\nThe 'pear' command is now at your service at $pear_cmd\n";
  86288. // Alert the user if the pear cmd is not in PATH
  86289. $old_dir = $pear_old ? dirname($pear_old) : false;
  86290. if (!$this->which('pear', $old_dir)) {
  86291. print "
  86292. ** The 'pear' command is not currently in your PATH, so you need to
  86293. ** use '$pear_cmd' until you have added
  86294. ** '$this->bin_dir' to your PATH environment variable.
  86295. ";
  86296. print "Run it without parameters to see the available actions, try 'pear list'
  86297. to see what packages are installed, or 'pear help' for help.
  86298. For more information about PEAR, see:
  86299. http://pear.php.net/faq.php
  86300. http://pear.php.net/manual/
  86301. Thanks for using go-pear!
  86302. ";
  86303. }
  86304. if (OS_WINDOWS && !$this->localInstall) {
  86305. $this->win32CreateRegEnv();
  86306. }
  86307. }
  86308. /**
  86309. * System::which() does not allow path exclusion
  86310. */
  86311. function which($program, $dont_search_in = false)
  86312. {
  86313. if (OS_WINDOWS) {
  86314. if ($_path = $this->safeGetEnv('Path')) {
  86315. $dirs = explode(';', $_path);
  86316. } else {
  86317. $dirs = explode(';', $this->safeGetEnv('PATH'));
  86318. }
  86319. foreach ($dirs as $i => $dir) {
  86320. $dirs[$i] = strtolower(realpath($dir));
  86321. }
  86322. if ($dont_search_in) {
  86323. $dont_search_in = strtolower(realpath($dont_search_in));
  86324. }
  86325. if ($dont_search_in &&
  86326. ($key = array_search($dont_search_in, $dirs)) !== false)
  86327. {
  86328. unset($dirs[$key]);
  86329. }
  86330. foreach ($dirs as $dir) {
  86331. $dir = str_replace('\\\\', '\\', $dir);
  86332. if (!strlen($dir)) {
  86333. continue;
  86334. }
  86335. if ($dir[strlen($dir) - 1] != '\\') {
  86336. $dir .= '\\';
  86337. }
  86338. $tmp = $dir . $program;
  86339. $info = pathinfo($tmp);
  86340. if (isset($info['extension']) && in_array(strtolower($info['extension']),
  86341. array('exe', 'com', 'bat', 'cmd'))) {
  86342. if (file_exists($tmp)) {
  86343. return strtolower($tmp);
  86344. }
  86345. } elseif (file_exists($ret = $tmp . '.exe') ||
  86346. file_exists($ret = $tmp . '.com') ||
  86347. file_exists($ret = $tmp . '.bat') ||
  86348. file_exists($ret = $tmp . '.cmd')) {
  86349. return strtolower($ret);
  86350. }
  86351. }
  86352. } else {
  86353. $dirs = explode(':', $this->safeGetEnv('PATH'));
  86354. if ($dont_search_in &&
  86355. ($key = array_search($dont_search_in, $dirs)) !== false)
  86356. {
  86357. unset($dirs[$key]);
  86358. }
  86359. foreach ($dirs as $dir) {
  86360. if (is_executable("$dir/$program")) {
  86361. return "$dir/$program";
  86362. }
  86363. }
  86364. }
  86365. return false;
  86366. }
  86367. /**
  86368. * Not optimized, but seems to work, if some nice
  86369. * peardev will test it? :)
  86370. *
  86371. * @author Pierre-Alain Joye <paj@pearfr.org>
  86372. */
  86373. function alterPhpIni($pathIni='')
  86374. {
  86375. $foundAt = array();
  86376. $iniSep = OS_WINDOWS ? ';' : ':';
  86377. if ($pathIni=='') {
  86378. $pathIni = $this->getPhpiniPath();
  86379. }
  86380. $arrayIni = file($pathIni);
  86381. $i=0;
  86382. $found=0;
  86383. // Looks for each active include_path directives
  86384. foreach ($arrayIni as $iniLine) {
  86385. $iniLine = trim($iniLine);
  86386. $iniLine = str_replace(array("\n", "\r"), array('', ''), $iniLine);
  86387. if (preg_match("/^\s*include_path/", $iniLine)) {
  86388. $foundAt[] = $i;
  86389. $found++;
  86390. }
  86391. $i++;
  86392. }
  86393. if ($found) {
  86394. $includeLine = $arrayIni[$foundAt[0]];
  86395. list(, $currentPath) = explode('=', $includeLine);
  86396. $currentPath = trim($currentPath);
  86397. if (substr($currentPath,0,1) == '"') {
  86398. $currentPath = substr($currentPath, 1, strlen($currentPath) - 2);
  86399. }
  86400. $arrayPath = explode($iniSep, $currentPath);
  86401. $newPath = array();
  86402. if ($arrayPath[0]=='.') {
  86403. $newPath[0] = '.';
  86404. $newPath[1] = $this->php_dir;
  86405. array_shift($arrayPath);
  86406. } else {
  86407. $newPath[0] = $this->php_dir;
  86408. }
  86409. foreach ($arrayPath as $path) {
  86410. $newPath[]= $path;
  86411. }
  86412. } else {
  86413. $newPath = array();
  86414. $newPath[0] = '.';
  86415. $newPath[1] = $this->php_dir;
  86416. $foundAt[] = count($arrayIni); // add a new line if none is present
  86417. }
  86418. $nl = OS_WINDOWS ? "\r\n" : "\n";
  86419. $includepath = 'include_path="' . implode($iniSep,$newPath) . '"';
  86420. $newInclude = "$nl$nl;***** Added by go-pear$nl" .
  86421. $includepath .
  86422. $nl . ";*****" .
  86423. $nl . $nl;
  86424. $arrayIni[$foundAt[0]] = $newInclude;
  86425. for ($i=1; $i<$found; $i++) {
  86426. $arrayIni[$foundAt[$i]]=';' . trim($arrayIni[$foundAt[$i]]);
  86427. }
  86428. $newIni = implode("", $arrayIni);
  86429. if (!($fh = @fopen($pathIni, "wb+"))) {
  86430. $prefixIni = $this->prefix . DIRECTORY_SEPARATOR . "php.ini-gopear";
  86431. $fh = fopen($prefixIni, "wb+");
  86432. if (!$fh) {
  86433. echo "
  86434. ******************************************************************************
  86435. WARNING: Cannot write to $pathIni nor in $this->prefix/php.ini-gopear. Please
  86436. modify manually your php.ini by adding:
  86437. $includepath
  86438. ";
  86439. return false;
  86440. } else {
  86441. fwrite($fh, $newIni, strlen($newIni));
  86442. fclose($fh);
  86443. echo "
  86444. ******************************************************************************
  86445. WARNING: Cannot write to $pathIni, but php.ini was successfully created
  86446. at <$this->prefix/php.ini-gopear>. Please replace the file <$pathIni> with
  86447. <$prefixIni> or modify your php.ini by adding:
  86448. $includepath
  86449. ";
  86450. }
  86451. } else {
  86452. fwrite($fh, $newIni, strlen($newIni));
  86453. fclose($fh);
  86454. echo "
  86455. php.ini <$pathIni> include_path updated.
  86456. ";
  86457. }
  86458. return true;
  86459. }
  86460. /**
  86461. * Generates a registry addOn for Win32 platform
  86462. * This addon set PEAR environment variables
  86463. * @author Pierrre-Alain Joye
  86464. */
  86465. function win32CreateRegEnv()
  86466. {
  86467. $nl = "\r\n";
  86468. $reg ='REGEDIT4'.$nl.
  86469. '[HKEY_CURRENT_USER\Environment]'. $nl .
  86470. '"PHP_PEAR_SYSCONF_DIR"="' . addslashes($this->prefix) . '"' . $nl .
  86471. '"PHP_PEAR_INSTALL_DIR"="' . addslashes($this->php_dir) . '"' . $nl .
  86472. '"PHP_PEAR_DOC_DIR"="' . addslashes($this->doc_dir) . '"' . $nl .
  86473. '"PHP_PEAR_BIN_DIR"="' . addslashes($this->bin_dir) . '"' . $nl .
  86474. '"PHP_PEAR_DATA_DIR"="' . addslashes($this->data_dir) . '"' . $nl .
  86475. '"PHP_PEAR_PHP_BIN"="' . addslashes($this->php_bin) . '"' . $nl .
  86476. '"PHP_PEAR_TEST_DIR"="' . addslashes($this->test_dir) . '"' . $nl;
  86477. $fh = fopen($this->prefix . DIRECTORY_SEPARATOR . 'PEAR_ENV.reg', 'wb');
  86478. if($fh){
  86479. fwrite($fh, $reg, strlen($reg));
  86480. fclose($fh);
  86481. echo "
  86482. * WINDOWS ENVIRONMENT VARIABLES *
  86483. For convenience, a REG file is available under {$this->prefix}PEAR_ENV.reg .
  86484. This file creates ENV variables for the current user.
  86485. Double-click this file to add it to the current user registry.
  86486. ";
  86487. }
  86488. }
  86489. function displayHTMLProgress()
  86490. {
  86491. }
  86492. }
  86493. ?>
  86494. <?php
  86495. /**
  86496. * PEAR_Task_Common, base class for installer tasks
  86497. *
  86498. * PHP versions 4 and 5
  86499. *
  86500. * @category pear
  86501. * @package PEAR
  86502. * @author Greg Beaver <cellog@php.net>
  86503. * @copyright 1997-2009 The Authors
  86504. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  86505. * @link http://pear.php.net/package/PEAR
  86506. * @since File available since Release 1.4.0a1
  86507. */
  86508. /**#@+
  86509. * Error codes for task validation routines
  86510. */
  86511. define('PEAR_TASK_ERROR_NOATTRIBS', 1);
  86512. define('PEAR_TASK_ERROR_MISSING_ATTRIB', 2);
  86513. define('PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE', 3);
  86514. define('PEAR_TASK_ERROR_INVALID', 4);
  86515. /**#@-*/
  86516. define('PEAR_TASK_PACKAGE', 1);
  86517. define('PEAR_TASK_INSTALL', 2);
  86518. define('PEAR_TASK_PACKAGEANDINSTALL', 3);
  86519. /**
  86520. * A task is an operation that manipulates the contents of a file.
  86521. *
  86522. * Simple tasks operate on 1 file. Multiple tasks are executed after all files have been
  86523. * processed and installed, and are designed to operate on all files containing the task.
  86524. * The Post-install script task simply takes advantage of the fact that it will be run
  86525. * after installation, replace is a simple task.
  86526. *
  86527. * Combining tasks is possible, but ordering is significant.
  86528. *
  86529. * <file name="test.php" role="php">
  86530. * <tasks:replace from="@data-dir@" to="data_dir" type="pear-config"/>
  86531. * <tasks:postinstallscript/>
  86532. * </file>
  86533. *
  86534. * This will first replace any instance of @data-dir@ in the test.php file
  86535. * with the path to the current data directory. Then, it will include the
  86536. * test.php file and run the script it contains to configure the package post-installation.
  86537. *
  86538. * @category pear
  86539. * @package PEAR
  86540. * @author Greg Beaver <cellog@php.net>
  86541. * @copyright 1997-2009 The Authors
  86542. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  86543. * @version Release: 1.10.16
  86544. * @link http://pear.php.net/package/PEAR
  86545. * @since Class available since Release 1.4.0a1
  86546. * @abstract
  86547. */
  86548. class PEAR_Task_Common
  86549. {
  86550. /**
  86551. * Valid types for this version are 'simple' and 'multiple'
  86552. *
  86553. * - simple tasks operate on the contents of a file and write out changes to disk
  86554. * - multiple tasks operate on the contents of many files and write out the
  86555. * changes directly to disk
  86556. *
  86557. * Child task classes must override this property.
  86558. *
  86559. * @access protected
  86560. */
  86561. protected $type = 'simple';
  86562. /**
  86563. * Determines which install phase this task is executed under
  86564. */
  86565. public $phase = PEAR_TASK_INSTALL;
  86566. /**
  86567. * @access protected
  86568. */
  86569. protected $config;
  86570. /**
  86571. * @access protected
  86572. */
  86573. protected $registry;
  86574. /**
  86575. * @access protected
  86576. */
  86577. public $logger;
  86578. /**
  86579. * @access protected
  86580. */
  86581. protected $installphase;
  86582. /**
  86583. * @param PEAR_Config
  86584. * @param PEAR_Common
  86585. */
  86586. function __construct(&$config, &$logger, $phase)
  86587. {
  86588. $this->config = &$config;
  86589. $this->registry = &$config->getRegistry();
  86590. $this->logger = &$logger;
  86591. $this->installphase = $phase;
  86592. if ($this->type == 'multiple') {
  86593. $GLOBALS['_PEAR_TASK_POSTINSTANCES'][get_class($this)][] = &$this;
  86594. }
  86595. }
  86596. /**
  86597. * Validate the basic contents of a task tag.
  86598. *
  86599. * @param PEAR_PackageFile_v2
  86600. * @param array
  86601. * @param PEAR_Config
  86602. * @param array the entire parsed <file> tag
  86603. *
  86604. * @return true|array On error, return an array in format:
  86605. * array(PEAR_TASK_ERROR_???[, param1][, param2][, ...])
  86606. *
  86607. * For PEAR_TASK_ERROR_MISSING_ATTRIB, pass the attribute name in
  86608. * For PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, pass the attribute name and
  86609. * an array of legal values in
  86610. *
  86611. * @abstract
  86612. */
  86613. public static function validateXml($pkg, $xml, $config, $fileXml)
  86614. {
  86615. }
  86616. /**
  86617. * Initialize a task instance with the parameters
  86618. *
  86619. * @param array raw, parsed xml
  86620. * @param array attributes from the <file> tag containing this task
  86621. * @param string|null last installed version of this package
  86622. * @abstract
  86623. */
  86624. public function init($xml, $fileAttributes, $lastVersion)
  86625. {
  86626. }
  86627. /**
  86628. * Begin a task processing session. All multiple tasks will be processed
  86629. * after each file has been successfully installed, all simple tasks should
  86630. * perform their task here and return any errors using the custom
  86631. * throwError() method to allow forward compatibility
  86632. *
  86633. * This method MUST NOT write out any changes to disk
  86634. *
  86635. * @param PEAR_PackageFile_v2
  86636. * @param string file contents
  86637. * @param string the eventual final file location (informational only)
  86638. * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
  86639. * (use $this->throwError), otherwise return the new contents
  86640. * @abstract
  86641. */
  86642. public function startSession($pkg, $contents, $dest)
  86643. {
  86644. }
  86645. /**
  86646. * This method is used to process each of the tasks for a particular
  86647. * multiple class type. Simple tasks need not implement this method.
  86648. *
  86649. * @param array an array of tasks
  86650. * @access protected
  86651. */
  86652. public static function run($tasks)
  86653. {
  86654. }
  86655. /**
  86656. * @final
  86657. */
  86658. public static function hasPostinstallTasks()
  86659. {
  86660. return isset($GLOBALS['_PEAR_TASK_POSTINSTANCES']);
  86661. }
  86662. /**
  86663. * @final
  86664. */
  86665. public static function runPostinstallTasks()
  86666. {
  86667. foreach ($GLOBALS['_PEAR_TASK_POSTINSTANCES'] as $class => $tasks) {
  86668. $err = call_user_func(
  86669. array($class, 'run'),
  86670. $GLOBALS['_PEAR_TASK_POSTINSTANCES'][$class]
  86671. );
  86672. if ($err) {
  86673. return PEAR_Task_Common::throwError($err);
  86674. }
  86675. }
  86676. unset($GLOBALS['_PEAR_TASK_POSTINSTANCES']);
  86677. }
  86678. /**
  86679. * Determines whether a role is a script
  86680. * @return bool
  86681. */
  86682. public function isScript()
  86683. {
  86684. return $this->type == 'script';
  86685. }
  86686. public function throwError($msg, $code = -1)
  86687. {
  86688. include_once 'phar://go-pear.phar/' . 'PEAR.php';
  86689. return PEAR::raiseError($msg, $code);
  86690. }
  86691. }
  86692. <?php
  86693. /**
  86694. * <tasks:postinstallscript>
  86695. *
  86696. * PHP versions 4 and 5
  86697. *
  86698. * @category pear
  86699. * @package PEAR
  86700. * @author Greg Beaver <cellog@php.net>
  86701. * @copyright 1997-2009 The Authors
  86702. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  86703. * @link http://pear.php.net/package/PEAR
  86704. * @since File available since Release 1.4.0a1
  86705. */
  86706. /**
  86707. * Base class
  86708. */
  86709. require_once 'phar://go-pear.phar/' . 'PEAR/Task/Common.php';
  86710. /**
  86711. * Implements the postinstallscript file task.
  86712. *
  86713. * Note that post-install scripts are handled separately from installation, by the
  86714. * "pear run-scripts" command
  86715. *
  86716. * @category pear
  86717. * @package PEAR
  86718. * @author Greg Beaver <cellog@php.net>
  86719. * @copyright 1997-2009 The Authors
  86720. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  86721. * @version Release: 1.10.16
  86722. * @link http://pear.php.net/package/PEAR
  86723. * @since Class available since Release 1.4.0a1
  86724. */
  86725. class PEAR_Task_Postinstallscript extends PEAR_Task_Common
  86726. {
  86727. public $type = 'script';
  86728. public $_class;
  86729. public $_params;
  86730. public $_obj;
  86731. /**
  86732. *
  86733. * @var PEAR_PackageFile_v2
  86734. */
  86735. public $_pkg;
  86736. public $_contents;
  86737. public $phase = PEAR_TASK_INSTALL;
  86738. /**
  86739. * Validate the raw xml at parsing-time.
  86740. *
  86741. * This also attempts to validate the script to make sure it meets the criteria
  86742. * for a post-install script
  86743. *
  86744. * @param PEAR_PackageFile_v2
  86745. * @param array The XML contents of the <postinstallscript> tag
  86746. * @param PEAR_Config
  86747. * @param array the entire parsed <file> tag
  86748. */
  86749. public static function validateXml($pkg, $xml, $config, $fileXml)
  86750. {
  86751. if ($fileXml['role'] != 'php') {
  86752. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  86753. $fileXml['name'].'" must be role="php"', );
  86754. }
  86755. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  86756. $file = $pkg->getFileContents($fileXml['name']);
  86757. if (PEAR::isError($file)) {
  86758. PEAR::popErrorHandling();
  86759. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  86760. $fileXml['name'].'" is not valid: '.
  86761. $file->getMessage(), );
  86762. } elseif ($file === null) {
  86763. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  86764. $fileXml['name'].'" could not be retrieved for processing!', );
  86765. } else {
  86766. $analysis = $pkg->analyzeSourceCode($file, true);
  86767. if (!$analysis) {
  86768. PEAR::popErrorHandling();
  86769. $warnings = '';
  86770. foreach ($pkg->getValidationWarnings() as $warn) {
  86771. $warnings .= $warn['message']."\n";
  86772. }
  86773. return array(PEAR_TASK_ERROR_INVALID, 'Analysis of post-install script "'.
  86774. $fileXml['name'].'" failed: '.$warnings, );
  86775. }
  86776. if (count($analysis['declared_classes']) != 1) {
  86777. PEAR::popErrorHandling();
  86778. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  86779. $fileXml['name'].'" must declare exactly 1 class', );
  86780. }
  86781. $class = $analysis['declared_classes'][0];
  86782. if ($class != str_replace(
  86783. array('/', '.php'), array('_', ''),
  86784. $fileXml['name']
  86785. ).'_postinstall') {
  86786. PEAR::popErrorHandling();
  86787. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  86788. $fileXml['name'].'" class "'.$class.'" must be named "'.
  86789. str_replace(
  86790. array('/', '.php'), array('_', ''),
  86791. $fileXml['name']
  86792. ).'_postinstall"', );
  86793. }
  86794. if (!isset($analysis['declared_methods'][$class])) {
  86795. PEAR::popErrorHandling();
  86796. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  86797. $fileXml['name'].'" must declare methods init() and run()', );
  86798. }
  86799. $methods = array('init' => 0, 'run' => 1);
  86800. foreach ($analysis['declared_methods'][$class] as $method) {
  86801. if (isset($methods[$method])) {
  86802. unset($methods[$method]);
  86803. }
  86804. }
  86805. if (count($methods)) {
  86806. PEAR::popErrorHandling();
  86807. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  86808. $fileXml['name'].'" must declare methods init() and run()', );
  86809. }
  86810. }
  86811. PEAR::popErrorHandling();
  86812. $definedparams = array();
  86813. $tasksNamespace = $pkg->getTasksNs().':';
  86814. if (!isset($xml[$tasksNamespace.'paramgroup']) && isset($xml['paramgroup'])) {
  86815. // in order to support the older betas, which did not expect internal tags
  86816. // to also use the namespace
  86817. $tasksNamespace = '';
  86818. }
  86819. if (isset($xml[$tasksNamespace.'paramgroup'])) {
  86820. $params = $xml[$tasksNamespace.'paramgroup'];
  86821. if (!is_array($params) || !isset($params[0])) {
  86822. $params = array($params);
  86823. }
  86824. foreach ($params as $param) {
  86825. if (!isset($param[$tasksNamespace.'id'])) {
  86826. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  86827. $fileXml['name'].'" <paramgroup> must have '.
  86828. 'an '.$tasksNamespace.'id> tag', );
  86829. }
  86830. if (isset($param[$tasksNamespace.'name'])) {
  86831. if (!in_array($param[$tasksNamespace.'name'], $definedparams)) {
  86832. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  86833. $fileXml['name'].'" '.$tasksNamespace.
  86834. 'paramgroup> id "'.$param[$tasksNamespace.'id'].
  86835. '" parameter "'.$param[$tasksNamespace.'name'].
  86836. '" has not been previously defined', );
  86837. }
  86838. if (!isset($param[$tasksNamespace.'conditiontype'])) {
  86839. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  86840. $fileXml['name'].'" '.$tasksNamespace.
  86841. 'paramgroup> id "'.$param[$tasksNamespace.'id'].
  86842. '" must have a '.$tasksNamespace.
  86843. 'conditiontype> tag containing either "=", '.
  86844. '"!=", or "preg_match"', );
  86845. }
  86846. if (!in_array(
  86847. $param[$tasksNamespace.'conditiontype'],
  86848. array('=', '!=', 'preg_match')
  86849. )) {
  86850. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  86851. $fileXml['name'].'" '.$tasksNamespace.
  86852. 'paramgroup> id "'.$param[$tasksNamespace.'id'].
  86853. '" must have a '.$tasksNamespace.
  86854. 'conditiontype> tag containing either "=", '.
  86855. '"!=", or "preg_match"', );
  86856. }
  86857. if (!isset($param[$tasksNamespace.'value'])) {
  86858. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  86859. $fileXml['name'].'" '.$tasksNamespace.
  86860. 'paramgroup> id "'.$param[$tasksNamespace.'id'].
  86861. '" must have a '.$tasksNamespace.
  86862. 'value> tag containing expected parameter value', );
  86863. }
  86864. }
  86865. if (isset($param[$tasksNamespace.'instructions'])) {
  86866. if (!is_string($param[$tasksNamespace.'instructions'])) {
  86867. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  86868. $fileXml['name'].'" '.$tasksNamespace.
  86869. 'paramgroup> id "'.$param[$tasksNamespace.'id'].
  86870. '" '.$tasksNamespace.'instructions> must be simple text', );
  86871. }
  86872. }
  86873. if (!isset($param[$tasksNamespace.'param'])) {
  86874. continue; // <param> is no longer required
  86875. }
  86876. $subparams = $param[$tasksNamespace.'param'];
  86877. if (!is_array($subparams) || !isset($subparams[0])) {
  86878. $subparams = array($subparams);
  86879. }
  86880. foreach ($subparams as $subparam) {
  86881. if (!isset($subparam[$tasksNamespace.'name'])) {
  86882. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  86883. $fileXml['name'].'" parameter for '.
  86884. $tasksNamespace.'paramgroup> id "'.
  86885. $param[$tasksNamespace.'id'].'" must have '.
  86886. 'a '.$tasksNamespace.'name> tag', );
  86887. }
  86888. if (!preg_match(
  86889. '/[a-zA-Z0-9]+/',
  86890. $subparam[$tasksNamespace.'name']
  86891. )) {
  86892. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  86893. $fileXml['name'].'" parameter "'.
  86894. $subparam[$tasksNamespace.'name'].
  86895. '" for '.$tasksNamespace.'paramgroup> id "'.
  86896. $param[$tasksNamespace.'id'].
  86897. '" is not a valid name. Must contain only alphanumeric characters', );
  86898. }
  86899. if (!isset($subparam[$tasksNamespace.'prompt'])) {
  86900. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  86901. $fileXml['name'].'" parameter "'.
  86902. $subparam[$tasksNamespace.'name'].
  86903. '" for '.$tasksNamespace.'paramgroup> id "'.
  86904. $param[$tasksNamespace.'id'].
  86905. '" must have a '.$tasksNamespace.'prompt> tag', );
  86906. }
  86907. if (!isset($subparam[$tasksNamespace.'type'])) {
  86908. return array(PEAR_TASK_ERROR_INVALID, 'Post-install script "'.
  86909. $fileXml['name'].'" parameter "'.
  86910. $subparam[$tasksNamespace.'name'].
  86911. '" for '.$tasksNamespace.'paramgroup> id "'.
  86912. $param[$tasksNamespace.'id'].
  86913. '" must have a '.$tasksNamespace.'type> tag', );
  86914. }
  86915. $definedparams[] = $param[$tasksNamespace.'id'].'::'.
  86916. $subparam[$tasksNamespace.'name'];
  86917. }
  86918. }
  86919. }
  86920. return true;
  86921. }
  86922. /**
  86923. * Initialize a task instance with the parameters
  86924. * @param array $xml raw, parsed xml
  86925. * @param array $fileattribs attributes from the <file> tag containing
  86926. * this task
  86927. * @param string|null $lastversion last installed version of this package,
  86928. * if any (useful for upgrades)
  86929. */
  86930. public function init($xml, $fileattribs, $lastversion)
  86931. {
  86932. $this->_class = str_replace('/', '_', $fileattribs['name']);
  86933. $this->_filename = $fileattribs['name'];
  86934. $this->_class = str_replace('.php', '', $this->_class).'_postinstall';
  86935. $this->_params = $xml;
  86936. $this->_lastversion = $lastversion;
  86937. }
  86938. /**
  86939. * Strip the tasks: namespace from internal params
  86940. *
  86941. * @access private
  86942. */
  86943. public function _stripNamespace($params = null)
  86944. {
  86945. if ($params === null) {
  86946. $params = array();
  86947. if (!is_array($this->_params)) {
  86948. return;
  86949. }
  86950. foreach ($this->_params as $i => $param) {
  86951. if (is_array($param)) {
  86952. $param = $this->_stripNamespace($param);
  86953. }
  86954. $params[str_replace($this->_pkg->getTasksNs().':', '', $i)] = $param;
  86955. }
  86956. $this->_params = $params;
  86957. } else {
  86958. $newparams = array();
  86959. foreach ($params as $i => $param) {
  86960. if (is_array($param)) {
  86961. $param = $this->_stripNamespace($param);
  86962. }
  86963. $newparams[str_replace($this->_pkg->getTasksNs().':', '', $i)] = $param;
  86964. }
  86965. return $newparams;
  86966. }
  86967. }
  86968. /**
  86969. * Unlike other tasks, the installed file name is passed in instead of the
  86970. * file contents, because this task is handled post-installation
  86971. *
  86972. * @param mixed $pkg PEAR_PackageFile_v1|PEAR_PackageFile_v2
  86973. * @param string $contents file name
  86974. * @param string $dest the eventual final file location (informational only)
  86975. *
  86976. * @return bool|PEAR_Error false to skip this file, PEAR_Error to fail
  86977. * (use $this->throwError)
  86978. */
  86979. public function startSession($pkg, $contents, $dest)
  86980. {
  86981. if ($this->installphase != PEAR_TASK_INSTALL) {
  86982. return false;
  86983. }
  86984. // remove the tasks: namespace if present
  86985. $this->_pkg = $pkg;
  86986. $this->_stripNamespace();
  86987. $this->logger->log(
  86988. 0, 'Including external post-installation script "'.
  86989. $contents.'" - any errors are in this script'
  86990. );
  86991. include_once 'phar://go-pear.phar/' . $contents;
  86992. if (class_exists($this->_class)) {
  86993. $this->logger->log(0, 'Inclusion succeeded');
  86994. } else {
  86995. return $this->throwError(
  86996. 'init of post-install script class "'.$this->_class
  86997. .'" failed'
  86998. );
  86999. }
  87000. $this->_obj = new $this->_class();
  87001. $this->logger->log(1, 'running post-install script "'.$this->_class.'->init()"');
  87002. PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  87003. $res = $this->_obj->init($this->config, $pkg, $this->_lastversion);
  87004. PEAR::popErrorHandling();
  87005. if ($res) {
  87006. $this->logger->log(0, 'init succeeded');
  87007. } else {
  87008. return $this->throwError(
  87009. 'init of post-install script "'.$this->_class.
  87010. '->init()" failed'
  87011. );
  87012. }
  87013. $this->_contents = $contents;
  87014. return true;
  87015. }
  87016. /**
  87017. * No longer used
  87018. *
  87019. * @see PEAR_PackageFile_v2::runPostinstallScripts()
  87020. * @param array an array of tasks
  87021. * @param string install or upgrade
  87022. * @access protected
  87023. */
  87024. public static function run($tasks)
  87025. {
  87026. }
  87027. }
  87028. <?php
  87029. /**
  87030. * <tasks:postinstallscript> - read/write version
  87031. *
  87032. * PHP versions 4 and 5
  87033. *
  87034. * @category pear
  87035. * @package PEAR
  87036. * @author Greg Beaver <cellog@php.net>
  87037. * @copyright 1997-2009 The Authors
  87038. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  87039. * @link http://pear.php.net/package/PEAR
  87040. * @since File available since Release 1.4.0a10
  87041. */
  87042. /**
  87043. * Base class
  87044. */
  87045. require_once 'phar://go-pear.phar/' . 'PEAR/Task/Postinstallscript.php';
  87046. /**
  87047. * Abstracts the postinstallscript file task xml.
  87048. * @category pear
  87049. * @package PEAR
  87050. * @author Greg Beaver <cellog@php.net>
  87051. * @copyright 1997-2009 The Authors
  87052. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  87053. * @version Release: 1.10.16
  87054. * @link http://pear.php.net/package/PEAR
  87055. * @since Class available since Release 1.4.0a10
  87056. */
  87057. class PEAR_Task_Postinstallscript_rw extends PEAR_Task_Postinstallscript
  87058. {
  87059. /**
  87060. * parent package file object
  87061. *
  87062. * @var PEAR_PackageFile_v2_rw
  87063. */
  87064. public $_pkg;
  87065. /**
  87066. * Enter description here...
  87067. *
  87068. * @param PEAR_PackageFile_v2_rw $pkg Package
  87069. * @param PEAR_Config $config Config
  87070. * @param PEAR_Frontend $logger Logger
  87071. * @param array $fileXml XML
  87072. *
  87073. * @return PEAR_Task_Postinstallscript_rw
  87074. */
  87075. function __construct(&$pkg, &$config, &$logger, $fileXml)
  87076. {
  87077. parent::__construct($config, $logger, PEAR_TASK_PACKAGE);
  87078. $this->_contents = $fileXml;
  87079. $this->_pkg = &$pkg;
  87080. $this->_params = array();
  87081. }
  87082. public function validate()
  87083. {
  87084. return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents);
  87085. }
  87086. public function getName()
  87087. {
  87088. return 'postinstallscript';
  87089. }
  87090. /**
  87091. * add a simple <paramgroup> to the post-install script
  87092. *
  87093. * Order is significant, so call this method in the same
  87094. * sequence the users should see the paramgroups. The $params
  87095. * parameter should either be the result of a call to {@link getParam()}
  87096. * or an array of calls to getParam().
  87097. *
  87098. * Use {@link addConditionTypeGroup()} to add a <paramgroup> containing
  87099. * a <conditiontype> tag
  87100. *
  87101. * @param string $id <paramgroup> id as seen by the script
  87102. * @param array|false $params array of getParam() calls, or false for no params
  87103. * @param string|false $instructions
  87104. */
  87105. public function addParamGroup($id, $params = false, $instructions = false)
  87106. {
  87107. if ($params && isset($params[0]) && !isset($params[1])) {
  87108. $params = $params[0];
  87109. }
  87110. $stuff =
  87111. array(
  87112. $this->_pkg->getTasksNs().':id' => $id,
  87113. );
  87114. if ($instructions) {
  87115. $stuff[$this->_pkg->getTasksNs().':instructions'] = $instructions;
  87116. }
  87117. if ($params) {
  87118. $stuff[$this->_pkg->getTasksNs().':param'] = $params;
  87119. }
  87120. $this->_params[$this->_pkg->getTasksNs().':paramgroup'][] = $stuff;
  87121. }
  87122. /**
  87123. * Add a complex <paramgroup> to the post-install script with conditions
  87124. *
  87125. * This inserts a <paramgroup> with
  87126. *
  87127. * Order is significant, so call this method in the same
  87128. * sequence the users should see the paramgroups. The $params
  87129. * parameter should either be the result of a call to {@link getParam()}
  87130. * or an array of calls to getParam().
  87131. *
  87132. * Use {@link addParamGroup()} to add a simple <paramgroup>
  87133. *
  87134. * @param string $id <paramgroup> id as seen by the script
  87135. * @param string $oldgroup <paramgroup> id of the section referenced by
  87136. * <conditiontype>
  87137. * @param string $param name of the <param> from the older section referenced
  87138. * by <contitiontype>
  87139. * @param string $value value to match of the parameter
  87140. * @param string $conditiontype one of '=', '!=', 'preg_match'
  87141. * @param array|false $params array of getParam() calls, or false for no params
  87142. * @param string|false $instructions
  87143. */
  87144. public function addConditionTypeGroup($id,
  87145. $oldgroup,
  87146. $param,
  87147. $value,
  87148. $conditiontype = '=',
  87149. $params = false,
  87150. $instructions = false
  87151. ) {
  87152. if ($params && isset($params[0]) && !isset($params[1])) {
  87153. $params = $params[0];
  87154. }
  87155. $stuff = array(
  87156. $this->_pkg->getTasksNs().':id' => $id,
  87157. );
  87158. if ($instructions) {
  87159. $stuff[$this->_pkg->getTasksNs().':instructions'] = $instructions;
  87160. }
  87161. $stuff[$this->_pkg->getTasksNs().':name'] = $oldgroup.'::'.$param;
  87162. $stuff[$this->_pkg->getTasksNs().':conditiontype'] = $conditiontype;
  87163. $stuff[$this->_pkg->getTasksNs().':value'] = $value;
  87164. if ($params) {
  87165. $stuff[$this->_pkg->getTasksNs().':param'] = $params;
  87166. }
  87167. $this->_params[$this->_pkg->getTasksNs().':paramgroup'][] = $stuff;
  87168. }
  87169. public function getXml()
  87170. {
  87171. return $this->_params;
  87172. }
  87173. /**
  87174. * Use to set up a param tag for use in creating a paramgroup
  87175. *
  87176. * @param mixed $name Name of parameter
  87177. * @param mixed $prompt Prompt
  87178. * @param string $type Type, defaults to 'string'
  87179. * @param mixed $default Default value
  87180. *
  87181. * @return array
  87182. */
  87183. public function getParam(
  87184. $name, $prompt, $type = 'string', $default = null
  87185. ) {
  87186. if ($default !== null) {
  87187. return
  87188. array(
  87189. $this->_pkg->getTasksNs().':name' => $name,
  87190. $this->_pkg->getTasksNs().':prompt' => $prompt,
  87191. $this->_pkg->getTasksNs().':type' => $type,
  87192. $this->_pkg->getTasksNs().':default' => $default,
  87193. );
  87194. }
  87195. return
  87196. array(
  87197. $this->_pkg->getTasksNs().':name' => $name,
  87198. $this->_pkg->getTasksNs().':prompt' => $prompt,
  87199. $this->_pkg->getTasksNs().':type' => $type,
  87200. );
  87201. }
  87202. }
  87203. <?php
  87204. /**
  87205. * <tasks:replace>
  87206. *
  87207. * PHP versions 4 and 5
  87208. *
  87209. * @category pear
  87210. * @package PEAR
  87211. * @author Greg Beaver <cellog@php.net>
  87212. * @copyright 1997-2009 The Authors
  87213. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  87214. * @link http://pear.php.net/package/PEAR
  87215. * @since File available since Release 1.4.0a1
  87216. */
  87217. /**
  87218. * Base class
  87219. */
  87220. require_once 'phar://go-pear.phar/' . 'PEAR/Task/Common.php';
  87221. /**
  87222. * Implements the replace file task.
  87223. * @category pear
  87224. * @package PEAR
  87225. * @author Greg Beaver <cellog@php.net>
  87226. * @copyright 1997-2009 The Authors
  87227. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  87228. * @version Release: 1.10.16
  87229. * @link http://pear.php.net/package/PEAR
  87230. * @since Class available since Release 1.4.0a1
  87231. */
  87232. class PEAR_Task_Replace extends PEAR_Task_Common
  87233. {
  87234. public $type = 'simple';
  87235. public $phase = PEAR_TASK_PACKAGEANDINSTALL;
  87236. public $_replacements;
  87237. /**
  87238. * Validate the raw xml at parsing-time.
  87239. *
  87240. * @param PEAR_PackageFile_v2
  87241. * @param array raw, parsed xml
  87242. * @param PEAR_Config
  87243. */
  87244. public static function validateXml($pkg, $xml, $config, $fileXml)
  87245. {
  87246. if (!isset($xml['attribs'])) {
  87247. return array(PEAR_TASK_ERROR_NOATTRIBS);
  87248. }
  87249. if (!isset($xml['attribs']['type'])) {
  87250. return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'type');
  87251. }
  87252. if (!isset($xml['attribs']['to'])) {
  87253. return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'to');
  87254. }
  87255. if (!isset($xml['attribs']['from'])) {
  87256. return array(PEAR_TASK_ERROR_MISSING_ATTRIB, 'from');
  87257. }
  87258. if ($xml['attribs']['type'] == 'pear-config') {
  87259. if (!in_array($xml['attribs']['to'], $config->getKeys())) {
  87260. return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
  87261. $config->getKeys(), );
  87262. }
  87263. } elseif ($xml['attribs']['type'] == 'php-const') {
  87264. if (defined($xml['attribs']['to'])) {
  87265. return true;
  87266. } else {
  87267. return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
  87268. array('valid PHP constant'), );
  87269. }
  87270. } elseif ($xml['attribs']['type'] == 'package-info') {
  87271. if (in_array(
  87272. $xml['attribs']['to'],
  87273. array('name', 'summary', 'channel', 'notes', 'extends', 'description',
  87274. 'release_notes', 'license', 'release-license', 'license-uri',
  87275. 'version', 'api-version', 'state', 'api-state', 'release_date',
  87276. 'date', 'time', )
  87277. )) {
  87278. return true;
  87279. } else {
  87280. return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'to', $xml['attribs']['to'],
  87281. array('name', 'summary', 'channel', 'notes', 'extends', 'description',
  87282. 'release_notes', 'license', 'release-license', 'license-uri',
  87283. 'version', 'api-version', 'state', 'api-state', 'release_date',
  87284. 'date', 'time', ), );
  87285. }
  87286. } else {
  87287. return array(PEAR_TASK_ERROR_WRONG_ATTRIB_VALUE, 'type', $xml['attribs']['type'],
  87288. array('pear-config', 'package-info', 'php-const'), );
  87289. }
  87290. return true;
  87291. }
  87292. /**
  87293. * Initialize a task instance with the parameters
  87294. * @param array raw, parsed xml
  87295. * @param unused
  87296. * @param unused
  87297. */
  87298. public function init($xml, $attribs, $lastVersion = null)
  87299. {
  87300. $this->_replacements = isset($xml['attribs']) ? array($xml) : $xml;
  87301. }
  87302. /**
  87303. * Do a package.xml 1.0 replacement, with additional package-info fields available
  87304. *
  87305. * See validateXml() source for the complete list of allowed fields
  87306. *
  87307. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  87308. * @param string file contents
  87309. * @param string the eventual final file location (informational only)
  87310. * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
  87311. * (use $this->throwError), otherwise return the new contents
  87312. */
  87313. public function startSession($pkg, $contents, $dest)
  87314. {
  87315. $subst_from = $subst_to = array();
  87316. foreach ($this->_replacements as $a) {
  87317. $a = $a['attribs'];
  87318. $to = '';
  87319. if ($a['type'] == 'pear-config') {
  87320. if ($this->installphase == PEAR_TASK_PACKAGE) {
  87321. return false;
  87322. }
  87323. if ($a['to'] == 'master_server') {
  87324. $chan = $this->registry->getChannel($pkg->getChannel());
  87325. if (!PEAR::isError($chan)) {
  87326. $to = $chan->getServer();
  87327. } else {
  87328. $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]");
  87329. return false;
  87330. }
  87331. } else {
  87332. if ($this->config->isDefinedLayer('ftp')) {
  87333. // try the remote config file first
  87334. $to = $this->config->get($a['to'], 'ftp', $pkg->getChannel());
  87335. if (is_null($to)) {
  87336. // then default to local
  87337. $to = $this->config->get($a['to'], null, $pkg->getChannel());
  87338. }
  87339. } else {
  87340. $to = $this->config->get($a['to'], null, $pkg->getChannel());
  87341. }
  87342. }
  87343. if (is_null($to)) {
  87344. $this->logger->log(0, "$dest: invalid pear-config replacement: $a[to]");
  87345. return false;
  87346. }
  87347. } elseif ($a['type'] == 'php-const') {
  87348. if ($this->installphase == PEAR_TASK_PACKAGE) {
  87349. return false;
  87350. }
  87351. if (defined($a['to'])) {
  87352. $to = constant($a['to']);
  87353. } else {
  87354. $this->logger->log(0, "$dest: invalid php-const replacement: $a[to]");
  87355. return false;
  87356. }
  87357. } else {
  87358. if ($t = $pkg->packageInfo($a['to'])) {
  87359. $to = $t;
  87360. } else {
  87361. $this->logger->log(0, "$dest: invalid package-info replacement: $a[to]");
  87362. return false;
  87363. }
  87364. }
  87365. if (!is_null($to)) {
  87366. $subst_from[] = $a['from'];
  87367. $subst_to[] = $to;
  87368. }
  87369. }
  87370. $this->logger->log(
  87371. 3, "doing ".sizeof($subst_from).
  87372. " substitution(s) for $dest"
  87373. );
  87374. if (sizeof($subst_from)) {
  87375. $contents = str_replace($subst_from, $subst_to, $contents);
  87376. }
  87377. return $contents;
  87378. }
  87379. }
  87380. <?php
  87381. /**
  87382. * <tasks:replace> - read/write version
  87383. *
  87384. * PHP versions 4 and 5
  87385. *
  87386. * @category pear
  87387. * @package PEAR
  87388. * @author Greg Beaver <cellog@php.net>
  87389. * @copyright 1997-2009 The Authors
  87390. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  87391. * @link http://pear.php.net/package/PEAR
  87392. * @since File available since Release 1.4.0a10
  87393. */
  87394. /**
  87395. * Base class
  87396. */
  87397. require_once 'phar://go-pear.phar/' . 'PEAR/Task/Replace.php';
  87398. /**
  87399. * Abstracts the replace task xml.
  87400. * @category pear
  87401. * @package PEAR
  87402. * @author Greg Beaver <cellog@php.net>
  87403. * @copyright 1997-2009 The Authors
  87404. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  87405. * @version Release: 1.10.16
  87406. * @link http://pear.php.net/package/PEAR
  87407. * @since Class available since Release 1.4.0a10
  87408. */
  87409. class PEAR_Task_Replace_rw extends PEAR_Task_Replace
  87410. {
  87411. public function __construct(&$pkg, &$config, &$logger, $fileXml)
  87412. {
  87413. parent::__construct($config, $logger, PEAR_TASK_PACKAGE);
  87414. $this->_contents = $fileXml;
  87415. $this->_pkg = &$pkg;
  87416. $this->_params = array();
  87417. }
  87418. public function validate()
  87419. {
  87420. return $this->validateXml($this->_pkg, $this->_params, $this->config, $this->_contents);
  87421. }
  87422. public function setInfo($from, $to, $type)
  87423. {
  87424. $this->_params = array('attribs' => array('from' => $from, 'to' => $to, 'type' => $type));
  87425. }
  87426. public function getName()
  87427. {
  87428. return 'replace';
  87429. }
  87430. public function getXml()
  87431. {
  87432. return $this->_params;
  87433. }
  87434. }
  87435. <?php
  87436. /**
  87437. * <tasks:unixeol>
  87438. *
  87439. * PHP versions 4 and 5
  87440. *
  87441. * @category pear
  87442. * @package PEAR
  87443. * @author Greg Beaver <cellog@php.net>
  87444. * @copyright 1997-2009 The Authors
  87445. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  87446. * @link http://pear.php.net/package/PEAR
  87447. * @since File available since Release 1.4.0a1
  87448. */
  87449. /**
  87450. * Base class
  87451. */
  87452. require_once 'phar://go-pear.phar/' . 'PEAR/Task/Common.php';
  87453. /**
  87454. * Implements the unix line endings file task.
  87455. * @category pear
  87456. * @package PEAR
  87457. * @author Greg Beaver <cellog@php.net>
  87458. * @copyright 1997-2009 The Authors
  87459. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  87460. * @version Release: 1.10.16
  87461. * @link http://pear.php.net/package/PEAR
  87462. * @since Class available since Release 1.4.0a1
  87463. */
  87464. class PEAR_Task_Unixeol extends PEAR_Task_Common
  87465. {
  87466. public $type = 'simple';
  87467. public $phase = PEAR_TASK_PACKAGE;
  87468. public $_replacements;
  87469. /**
  87470. * Validate the raw xml at parsing-time.
  87471. *
  87472. * @param PEAR_PackageFile_v2
  87473. * @param array raw, parsed xml
  87474. * @param PEAR_Config
  87475. */
  87476. public static function validateXml($pkg, $xml, $config, $fileXml)
  87477. {
  87478. if ($xml != '') {
  87479. return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed');
  87480. }
  87481. return true;
  87482. }
  87483. /**
  87484. * Initialize a task instance with the parameters
  87485. * @param array raw, parsed xml
  87486. * @param unused
  87487. * @param unused
  87488. */
  87489. public function init($xml, $attribs, $lastVersion = null)
  87490. {
  87491. }
  87492. /**
  87493. * Replace all line endings with line endings customized for the current OS
  87494. *
  87495. * See validateXml() source for the complete list of allowed fields
  87496. *
  87497. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  87498. * @param string file contents
  87499. * @param string the eventual final file location (informational only)
  87500. * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
  87501. * (use $this->throwError), otherwise return the new contents
  87502. */
  87503. public function startSession($pkg, $contents, $dest)
  87504. {
  87505. $this->logger->log(3, "replacing all line endings with \\n in $dest");
  87506. return preg_replace("/\r\n|\n\r|\r|\n/", "\n", $contents);
  87507. }
  87508. }
  87509. <?php
  87510. /**
  87511. * <tasks:unixeol> - read/write version
  87512. *
  87513. * PHP versions 4 and 5
  87514. *
  87515. * @category pear
  87516. * @package PEAR
  87517. * @author Greg Beaver <cellog@php.net>
  87518. * @copyright 1997-2009 The Authors
  87519. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  87520. * @link http://pear.php.net/package/PEAR
  87521. * @since File available since Release 1.4.0a10
  87522. */
  87523. /**
  87524. * Base class
  87525. */
  87526. require_once 'phar://go-pear.phar/' . 'PEAR/Task/Unixeol.php';
  87527. /**
  87528. * Abstracts the unixeol task xml.
  87529. * @category pear
  87530. * @package PEAR
  87531. * @author Greg Beaver <cellog@php.net>
  87532. * @copyright 1997-2009 The Authors
  87533. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  87534. * @version Release: 1.10.16
  87535. * @link http://pear.php.net/package/PEAR
  87536. * @since Class available since Release 1.4.0a10
  87537. */
  87538. class PEAR_Task_Unixeol_rw extends PEAR_Task_Unixeol
  87539. {
  87540. function __construct(&$pkg, &$config, &$logger, $fileXml)
  87541. {
  87542. parent::__construct($config, $logger, PEAR_TASK_PACKAGE);
  87543. $this->_contents = $fileXml;
  87544. $this->_pkg = &$pkg;
  87545. $this->_params = array();
  87546. }
  87547. public function validate()
  87548. {
  87549. return true;
  87550. }
  87551. public function getName()
  87552. {
  87553. return 'unixeol';
  87554. }
  87555. public function getXml()
  87556. {
  87557. return '';
  87558. }
  87559. }
  87560. ?>
  87561. <?php
  87562. /**
  87563. * <tasks:windowseol>
  87564. *
  87565. * PHP versions 4 and 5
  87566. *
  87567. * @category pear
  87568. * @package PEAR
  87569. * @author Greg Beaver <cellog@php.net>
  87570. * @copyright 1997-2009 The Authors
  87571. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  87572. * @link http://pear.php.net/package/PEAR
  87573. * @since File available since Release 1.4.0a1
  87574. */
  87575. /**
  87576. * Base class
  87577. */
  87578. require_once 'phar://go-pear.phar/' . 'PEAR/Task/Common.php';
  87579. /**
  87580. * Implements the windows line endsings file task.
  87581. *
  87582. * @category pear
  87583. * @package PEAR
  87584. * @author Greg Beaver <cellog@php.net>
  87585. * @copyright 1997-2009 The Authors
  87586. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  87587. * @version Release: 1.10.16
  87588. * @link http://pear.php.net/package/PEAR
  87589. * @since Class available since Release 1.4.0a1
  87590. */
  87591. class PEAR_Task_Windowseol extends PEAR_Task_Common
  87592. {
  87593. public $type = 'simple';
  87594. public $phase = PEAR_TASK_PACKAGE;
  87595. public $_replacements;
  87596. /**
  87597. * Validate the raw xml at parsing-time.
  87598. *
  87599. * @param PEAR_PackageFile_v2
  87600. * @param array raw, parsed xml
  87601. * @param PEAR_Config
  87602. */
  87603. public static function validateXml($pkg, $xml, $config, $fileXml)
  87604. {
  87605. if ($xml != '') {
  87606. return array(PEAR_TASK_ERROR_INVALID, 'no attributes allowed');
  87607. }
  87608. return true;
  87609. }
  87610. /**
  87611. * Initialize a task instance with the parameters
  87612. * @param array raw, parsed xml
  87613. * @param unused
  87614. * @param unused
  87615. */
  87616. public function init($xml, $attribs, $lastVersion = null)
  87617. {
  87618. }
  87619. /**
  87620. * Replace all line endings with windows line endings
  87621. *
  87622. * See validateXml() source for the complete list of allowed fields
  87623. *
  87624. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  87625. * @param string file contents
  87626. * @param string the eventual final file location (informational only)
  87627. * @return string|false|PEAR_Error false to skip this file, PEAR_Error to fail
  87628. * (use $this->throwError), otherwise return the new contents
  87629. */
  87630. public function startSession($pkg, $contents, $dest)
  87631. {
  87632. $this->logger->log(3, "replacing all line endings with \\r\\n in $dest");
  87633. return preg_replace("/\r\n|\n\r|\r|\n/", "\r\n", $contents);
  87634. }
  87635. }
  87636. <?php
  87637. /**
  87638. * <tasks:windowseol> - read/write version
  87639. *
  87640. * PHP versions 4 and 5
  87641. *
  87642. * @category pear
  87643. * @package PEAR
  87644. * @author Greg Beaver <cellog@php.net>
  87645. * @copyright 1997-2009 The Authors
  87646. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  87647. * @link http://pear.php.net/package/PEAR
  87648. * @since File available since Release 1.4.0a10
  87649. */
  87650. /**
  87651. * Base class
  87652. */
  87653. require_once 'phar://go-pear.phar/' . 'PEAR/Task/Windowseol.php';
  87654. /**
  87655. * Abstracts the windowseol task xml.
  87656. *
  87657. * @category pear
  87658. * @package PEAR
  87659. * @author Greg Beaver <cellog@php.net>
  87660. * @copyright 1997-2009 The Authors
  87661. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  87662. * @version Release: 1.10.16
  87663. * @link http://pear.php.net/package/PEAR
  87664. * @since Class available since Release 1.4.0a10
  87665. */
  87666. class PEAR_Task_Windowseol_rw extends PEAR_Task_Windowseol
  87667. {
  87668. function __construct(&$pkg, &$config, &$logger, $fileXml)
  87669. {
  87670. parent::__construct($config, $logger, PEAR_TASK_PACKAGE);
  87671. $this->_contents = $fileXml;
  87672. $this->_pkg = &$pkg;
  87673. $this->_params = array();
  87674. }
  87675. public function validate()
  87676. {
  87677. return true;
  87678. }
  87679. public function getName()
  87680. {
  87681. return 'windowseol';
  87682. }
  87683. public function getXml()
  87684. {
  87685. return '';
  87686. }
  87687. }
  87688. ?>
  87689. <?php
  87690. /**
  87691. * PEAR_Validate
  87692. *
  87693. * PHP versions 4 and 5
  87694. *
  87695. * @category pear
  87696. * @package PEAR
  87697. * @author Greg Beaver <cellog@php.net>
  87698. * @copyright 1997-2009 The Authors
  87699. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  87700. * @link http://pear.php.net/package/PEAR
  87701. * @since File available since Release 1.4.0a1
  87702. */
  87703. /**#@+
  87704. * Constants for install stage
  87705. */
  87706. define('PEAR_VALIDATE_INSTALLING', 1);
  87707. define('PEAR_VALIDATE_UNINSTALLING', 2); // this is not bit-mapped like the others
  87708. define('PEAR_VALIDATE_NORMAL', 3);
  87709. define('PEAR_VALIDATE_DOWNLOADING', 4); // this is not bit-mapped like the others
  87710. define('PEAR_VALIDATE_PACKAGING', 7);
  87711. /**#@-*/
  87712. require_once 'phar://go-pear.phar/' . 'PEAR/Common.php';
  87713. require_once 'phar://go-pear.phar/' . 'PEAR/Validator/PECL.php';
  87714. /**
  87715. * Validation class for package.xml - channel-level advanced validation
  87716. * @category pear
  87717. * @package PEAR
  87718. * @author Greg Beaver <cellog@php.net>
  87719. * @copyright 1997-2009 The Authors
  87720. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  87721. * @version Release: 1.10.16
  87722. * @link http://pear.php.net/package/PEAR
  87723. * @since Class available since Release 1.4.0a1
  87724. */
  87725. class PEAR_Validate
  87726. {
  87727. var $packageregex = _PEAR_COMMON_PACKAGE_NAME_PREG;
  87728. /**
  87729. * @var PEAR_PackageFile_v1|PEAR_PackageFile_v2
  87730. */
  87731. var $_packagexml;
  87732. /**
  87733. * @var int one of the PEAR_VALIDATE_* constants
  87734. */
  87735. var $_state = PEAR_VALIDATE_NORMAL;
  87736. /**
  87737. * Format: ('error' => array('field' => name, 'reason' => reason), 'warning' => same)
  87738. * @var array
  87739. * @access private
  87740. */
  87741. var $_failures = array('error' => array(), 'warning' => array());
  87742. /**
  87743. * Override this method to handle validation of normal package names
  87744. * @param string
  87745. * @return bool
  87746. * @access protected
  87747. */
  87748. function _validPackageName($name)
  87749. {
  87750. return (bool) preg_match('/^' . $this->packageregex . '\\z/', $name);
  87751. }
  87752. /**
  87753. * @param string package name to validate
  87754. * @param string name of channel-specific validation package
  87755. * @final
  87756. */
  87757. function validPackageName($name, $validatepackagename = false)
  87758. {
  87759. if ($validatepackagename) {
  87760. if (strtolower($name) == strtolower($validatepackagename)) {
  87761. return (bool) preg_match('/^[a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+)*\\z/', $name);
  87762. }
  87763. }
  87764. return $this->_validPackageName($name);
  87765. }
  87766. /**
  87767. * This validates a bundle name, and bundle names must conform
  87768. * to the PEAR naming convention, so the method is final and static.
  87769. * @param string
  87770. * @final
  87771. */
  87772. public static function validGroupName($name)
  87773. {
  87774. return (bool) preg_match('/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '\\z/', $name);
  87775. }
  87776. /**
  87777. * Determine whether $state represents a valid stability level
  87778. * @param string
  87779. * @return bool
  87780. * @final
  87781. */
  87782. public static function validState($state)
  87783. {
  87784. return in_array($state, array('snapshot', 'devel', 'alpha', 'beta', 'stable'));
  87785. }
  87786. /**
  87787. * Get a list of valid stability levels
  87788. * @return array
  87789. * @final
  87790. */
  87791. public static function getValidStates()
  87792. {
  87793. return array('snapshot', 'devel', 'alpha', 'beta', 'stable');
  87794. }
  87795. /**
  87796. * Determine whether a version is a properly formatted version number that can be used
  87797. * by version_compare
  87798. * @param string
  87799. * @return bool
  87800. * @final
  87801. */
  87802. public static function validVersion($ver)
  87803. {
  87804. return (bool) preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
  87805. }
  87806. /**
  87807. * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
  87808. */
  87809. function setPackageFile(&$pf)
  87810. {
  87811. $this->_packagexml = &$pf;
  87812. }
  87813. /**
  87814. * @access private
  87815. */
  87816. function _addFailure($field, $reason)
  87817. {
  87818. $this->_failures['errors'][] = array('field' => $field, 'reason' => $reason);
  87819. }
  87820. /**
  87821. * @access private
  87822. */
  87823. function _addWarning($field, $reason)
  87824. {
  87825. $this->_failures['warnings'][] = array('field' => $field, 'reason' => $reason);
  87826. }
  87827. function getFailures()
  87828. {
  87829. $failures = $this->_failures;
  87830. $this->_failures = array('warnings' => array(), 'errors' => array());
  87831. return $failures;
  87832. }
  87833. /**
  87834. * @param int one of the PEAR_VALIDATE_* constants
  87835. */
  87836. function validate($state = null)
  87837. {
  87838. if (!isset($this->_packagexml)) {
  87839. return false;
  87840. }
  87841. if ($state !== null) {
  87842. $this->_state = $state;
  87843. }
  87844. $this->_failures = array('warnings' => array(), 'errors' => array());
  87845. $this->validatePackageName();
  87846. $this->validateVersion();
  87847. $this->validateMaintainers();
  87848. $this->validateDate();
  87849. $this->validateSummary();
  87850. $this->validateDescription();
  87851. $this->validateLicense();
  87852. $this->validateNotes();
  87853. if ($this->_packagexml->getPackagexmlVersion() == '1.0') {
  87854. $this->validateState();
  87855. $this->validateFilelist();
  87856. } elseif ($this->_packagexml->getPackagexmlVersion() == '2.0' ||
  87857. $this->_packagexml->getPackagexmlVersion() == '2.1') {
  87858. $this->validateTime();
  87859. $this->validateStability();
  87860. $this->validateDeps();
  87861. $this->validateMainFilelist();
  87862. $this->validateReleaseFilelist();
  87863. //$this->validateGlobalTasks();
  87864. $this->validateChangelog();
  87865. }
  87866. return !((bool) count($this->_failures['errors']));
  87867. }
  87868. /**
  87869. * @access protected
  87870. */
  87871. function validatePackageName()
  87872. {
  87873. if ($this->_state == PEAR_VALIDATE_PACKAGING ||
  87874. $this->_state == PEAR_VALIDATE_NORMAL) {
  87875. if (($this->_packagexml->getPackagexmlVersion() == '2.0' ||
  87876. $this->_packagexml->getPackagexmlVersion() == '2.1') &&
  87877. $this->_packagexml->getExtends()) {
  87878. $version = $this->_packagexml->getVersion() . '';
  87879. $name = $this->_packagexml->getPackage();
  87880. $a = explode('.', $version);
  87881. $test = array_shift($a);
  87882. if ($test == '0') {
  87883. return true;
  87884. }
  87885. $vlen = strlen($test);
  87886. $majver = substr($name, strlen($name) - $vlen);
  87887. while ($majver && !is_numeric($majver[0])) {
  87888. $majver = substr($majver, 1);
  87889. }
  87890. if ($majver != $test) {
  87891. $this->_addWarning('package', "package $name extends package " .
  87892. $this->_packagexml->getExtends() . ' and so the name should ' .
  87893. 'have a postfix equal to the major version like "' .
  87894. $this->_packagexml->getExtends() . $test . '"');
  87895. return true;
  87896. } elseif (substr($name, 0, strlen($name) - $vlen) !=
  87897. $this->_packagexml->getExtends()) {
  87898. $this->_addWarning('package', "package $name extends package " .
  87899. $this->_packagexml->getExtends() . ' and so the name must ' .
  87900. 'be an extension like "' . $this->_packagexml->getExtends() .
  87901. $test . '"');
  87902. return true;
  87903. }
  87904. }
  87905. }
  87906. if (!$this->validPackageName($this->_packagexml->getPackage())) {
  87907. $this->_addFailure('name', 'package name "' .
  87908. $this->_packagexml->getPackage() . '" is invalid');
  87909. return false;
  87910. }
  87911. }
  87912. /**
  87913. * @access protected
  87914. */
  87915. function validateVersion()
  87916. {
  87917. if ($this->_state != PEAR_VALIDATE_PACKAGING) {
  87918. if (!$this->validVersion($this->_packagexml->getVersion())) {
  87919. $this->_addFailure('version',
  87920. 'Invalid version number "' . $this->_packagexml->getVersion() . '"');
  87921. }
  87922. return false;
  87923. }
  87924. $version = $this->_packagexml->getVersion();
  87925. $versioncomponents = explode('.', $version);
  87926. if (count($versioncomponents) != 3) {
  87927. $this->_addWarning('version',
  87928. 'A version number should have 3 decimals (x.y.z)');
  87929. return true;
  87930. }
  87931. $name = $this->_packagexml->getPackage();
  87932. // version must be based upon state
  87933. switch ($this->_packagexml->getState()) {
  87934. case 'snapshot' :
  87935. return true;
  87936. case 'devel' :
  87937. if ($versioncomponents[0] . 'a' == '0a') {
  87938. return true;
  87939. }
  87940. if ($versioncomponents[0] == 0) {
  87941. $versioncomponents[0] = '0';
  87942. $this->_addWarning('version',
  87943. 'version "' . $version . '" should be "' .
  87944. implode('.' ,$versioncomponents) . '"');
  87945. } else {
  87946. $this->_addWarning('version',
  87947. 'packages with devel stability must be < version 1.0.0');
  87948. }
  87949. return true;
  87950. break;
  87951. case 'alpha' :
  87952. case 'beta' :
  87953. // check for a package that extends a package,
  87954. // like Foo and Foo2
  87955. if ($this->_state == PEAR_VALIDATE_PACKAGING) {
  87956. if (substr($versioncomponents[2], 1, 2) == 'rc') {
  87957. $this->_addFailure('version', 'Release Candidate versions ' .
  87958. 'must have capital RC, not lower-case rc');
  87959. return false;
  87960. }
  87961. }
  87962. if (!$this->_packagexml->getExtends()) {
  87963. if ($versioncomponents[0] == '1') {
  87964. if ($versioncomponents[2][0] == '0') {
  87965. if ($versioncomponents[2] == '0') {
  87966. // version 1.*.0000
  87967. $this->_addWarning('version',
  87968. 'version 1.' . $versioncomponents[1] .
  87969. '.0 probably should not be alpha or beta');
  87970. return true;
  87971. } elseif (strlen($versioncomponents[2]) > 1) {
  87972. // version 1.*.0RC1 or 1.*.0beta24 etc.
  87973. return true;
  87974. } else {
  87975. // version 1.*.0
  87976. $this->_addWarning('version',
  87977. 'version 1.' . $versioncomponents[1] .
  87978. '.0 probably should not be alpha or beta');
  87979. return true;
  87980. }
  87981. } else {
  87982. $this->_addWarning('version',
  87983. 'bugfix versions (1.3.x where x > 0) probably should ' .
  87984. 'not be alpha or beta');
  87985. return true;
  87986. }
  87987. } elseif ($versioncomponents[0] != '0') {
  87988. $this->_addWarning('version',
  87989. 'major versions greater than 1 are not allowed for packages ' .
  87990. 'without an <extends> tag or an identical postfix (foo2 v2.0.0)');
  87991. return true;
  87992. }
  87993. if ($versioncomponents[0] . 'a' == '0a') {
  87994. return true;
  87995. }
  87996. if ($versioncomponents[0] == 0) {
  87997. $versioncomponents[0] = '0';
  87998. $this->_addWarning('version',
  87999. 'version "' . $version . '" should be "' .
  88000. implode('.' ,$versioncomponents) . '"');
  88001. }
  88002. } else {
  88003. $vlen = strlen($versioncomponents[0] . '');
  88004. $majver = substr($name, strlen($name) - $vlen);
  88005. while ($majver && !is_numeric($majver[0])) {
  88006. $majver = substr($majver, 1);
  88007. }
  88008. if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) {
  88009. $this->_addWarning('version', 'first version number "' .
  88010. $versioncomponents[0] . '" must match the postfix of ' .
  88011. 'package name "' . $name . '" (' .
  88012. $majver . ')');
  88013. return true;
  88014. }
  88015. if ($versioncomponents[0] == $majver) {
  88016. if ($versioncomponents[2][0] == '0') {
  88017. if ($versioncomponents[2] == '0') {
  88018. // version 2.*.0000
  88019. $this->_addWarning('version',
  88020. "version $majver." . $versioncomponents[1] .
  88021. '.0 probably should not be alpha or beta');
  88022. return false;
  88023. } elseif (strlen($versioncomponents[2]) > 1) {
  88024. // version 2.*.0RC1 or 2.*.0beta24 etc.
  88025. return true;
  88026. } else {
  88027. // version 2.*.0
  88028. $this->_addWarning('version',
  88029. "version $majver." . $versioncomponents[1] .
  88030. '.0 cannot be alpha or beta');
  88031. return true;
  88032. }
  88033. } else {
  88034. $this->_addWarning('version',
  88035. "bugfix versions ($majver.x.y where y > 0) should " .
  88036. 'not be alpha or beta');
  88037. return true;
  88038. }
  88039. } elseif ($versioncomponents[0] != '0') {
  88040. $this->_addWarning('version',
  88041. "only versions 0.x.y and $majver.x.y are allowed for alpha/beta releases");
  88042. return true;
  88043. }
  88044. if ($versioncomponents[0] . 'a' == '0a') {
  88045. return true;
  88046. }
  88047. if ($versioncomponents[0] == 0) {
  88048. $versioncomponents[0] = '0';
  88049. $this->_addWarning('version',
  88050. 'version "' . $version . '" should be "' .
  88051. implode('.' ,$versioncomponents) . '"');
  88052. }
  88053. }
  88054. return true;
  88055. break;
  88056. case 'stable' :
  88057. if ($versioncomponents[0] == '0') {
  88058. $this->_addWarning('version', 'versions less than 1.0.0 cannot ' .
  88059. 'be stable');
  88060. return true;
  88061. }
  88062. if (!is_numeric($versioncomponents[2])) {
  88063. if (preg_match('/\d+(rc|a|alpha|b|beta)\d*/i',
  88064. $versioncomponents[2])) {
  88065. $this->_addWarning('version', 'version "' . $version . '" or any ' .
  88066. 'RC/beta/alpha version cannot be stable');
  88067. return true;
  88068. }
  88069. }
  88070. // check for a package that extends a package,
  88071. // like Foo and Foo2
  88072. if ($this->_packagexml->getExtends()) {
  88073. $vlen = strlen($versioncomponents[0] . '');
  88074. $majver = substr($name, strlen($name) - $vlen);
  88075. while ($majver && !is_numeric($majver[0])) {
  88076. $majver = substr($majver, 1);
  88077. }
  88078. if (($versioncomponents[0] != 0) && $majver != $versioncomponents[0]) {
  88079. $this->_addWarning('version', 'first version number "' .
  88080. $versioncomponents[0] . '" must match the postfix of ' .
  88081. 'package name "' . $name . '" (' .
  88082. $majver . ')');
  88083. return true;
  88084. }
  88085. } elseif ($versioncomponents[0] > 1) {
  88086. $this->_addWarning('version', 'major version x in x.y.z may not be greater than ' .
  88087. '1 for any package that does not have an <extends> tag');
  88088. }
  88089. return true;
  88090. break;
  88091. default :
  88092. return false;
  88093. break;
  88094. }
  88095. }
  88096. /**
  88097. * @access protected
  88098. */
  88099. function validateMaintainers()
  88100. {
  88101. // maintainers can only be truly validated server-side for most channels
  88102. // but allow this customization for those who wish it
  88103. return true;
  88104. }
  88105. /**
  88106. * @access protected
  88107. */
  88108. function validateDate()
  88109. {
  88110. if ($this->_state == PEAR_VALIDATE_NORMAL ||
  88111. $this->_state == PEAR_VALIDATE_PACKAGING) {
  88112. if (!preg_match('/(\d\d\d\d)\-(\d\d)\-(\d\d)/',
  88113. $this->_packagexml->getDate(), $res) ||
  88114. count($res) < 4
  88115. || !checkdate($res[2], $res[3], $res[1])
  88116. ) {
  88117. $this->_addFailure('date', 'invalid release date "' .
  88118. $this->_packagexml->getDate() . '"');
  88119. return false;
  88120. }
  88121. if ($this->_state == PEAR_VALIDATE_PACKAGING &&
  88122. $this->_packagexml->getDate() != date('Y-m-d')) {
  88123. $this->_addWarning('date', 'Release Date "' .
  88124. $this->_packagexml->getDate() . '" is not today');
  88125. }
  88126. }
  88127. return true;
  88128. }
  88129. /**
  88130. * @access protected
  88131. */
  88132. function validateTime()
  88133. {
  88134. if (!$this->_packagexml->getTime()) {
  88135. // default of no time value set
  88136. return true;
  88137. }
  88138. // packager automatically sets time, so only validate if pear validate is called
  88139. if ($this->_state = PEAR_VALIDATE_NORMAL) {
  88140. if (!preg_match('/\d\d:\d\d:\d\d/',
  88141. $this->_packagexml->getTime())) {
  88142. $this->_addFailure('time', 'invalid release time "' .
  88143. $this->_packagexml->getTime() . '"');
  88144. return false;
  88145. }
  88146. $result = preg_match('|\d{2}\:\d{2}\:\d{2}|', $this->_packagexml->getTime(), $matches);
  88147. if ($result === false || empty($matches)) {
  88148. $this->_addFailure('time', 'invalid release time "' .
  88149. $this->_packagexml->getTime() . '"');
  88150. return false;
  88151. }
  88152. }
  88153. return true;
  88154. }
  88155. /**
  88156. * @access protected
  88157. */
  88158. function validateState()
  88159. {
  88160. // this is the closest to "final" php4 can get
  88161. if (!PEAR_Validate::validState($this->_packagexml->getState())) {
  88162. if (strtolower($this->_packagexml->getState() == 'rc')) {
  88163. $this->_addFailure('state', 'RC is not a state, it is a version ' .
  88164. 'postfix, use ' . $this->_packagexml->getVersion() . 'RC1, state beta');
  88165. }
  88166. $this->_addFailure('state', 'invalid release state "' .
  88167. $this->_packagexml->getState() . '", must be one of: ' .
  88168. implode(', ', PEAR_Validate::getValidStates()));
  88169. return false;
  88170. }
  88171. return true;
  88172. }
  88173. /**
  88174. * @access protected
  88175. */
  88176. function validateStability()
  88177. {
  88178. $ret = true;
  88179. $packagestability = $this->_packagexml->getState();
  88180. $apistability = $this->_packagexml->getState('api');
  88181. if (!PEAR_Validate::validState($packagestability)) {
  88182. $this->_addFailure('state', 'invalid release stability "' .
  88183. $this->_packagexml->getState() . '", must be one of: ' .
  88184. implode(', ', PEAR_Validate::getValidStates()));
  88185. $ret = false;
  88186. }
  88187. $apistates = PEAR_Validate::getValidStates();
  88188. array_shift($apistates); // snapshot is not allowed
  88189. if (!in_array($apistability, $apistates)) {
  88190. $this->_addFailure('state', 'invalid API stability "' .
  88191. $this->_packagexml->getState('api') . '", must be one of: ' .
  88192. implode(', ', $apistates));
  88193. $ret = false;
  88194. }
  88195. return $ret;
  88196. }
  88197. /**
  88198. * @access protected
  88199. */
  88200. function validateSummary()
  88201. {
  88202. return true;
  88203. }
  88204. /**
  88205. * @access protected
  88206. */
  88207. function validateDescription()
  88208. {
  88209. return true;
  88210. }
  88211. /**
  88212. * @access protected
  88213. */
  88214. function validateLicense()
  88215. {
  88216. return true;
  88217. }
  88218. /**
  88219. * @access protected
  88220. */
  88221. function validateNotes()
  88222. {
  88223. return true;
  88224. }
  88225. /**
  88226. * for package.xml 2.0 only - channels can't use package.xml 1.0
  88227. * @access protected
  88228. */
  88229. function validateDependencies()
  88230. {
  88231. return true;
  88232. }
  88233. /**
  88234. * for package.xml 1.0 only
  88235. * @access private
  88236. */
  88237. function _validateFilelist()
  88238. {
  88239. return true; // placeholder for now
  88240. }
  88241. /**
  88242. * for package.xml 2.0 only
  88243. * @access protected
  88244. */
  88245. function validateMainFilelist()
  88246. {
  88247. return true; // placeholder for now
  88248. }
  88249. /**
  88250. * for package.xml 2.0 only
  88251. * @access protected
  88252. */
  88253. function validateReleaseFilelist()
  88254. {
  88255. return true; // placeholder for now
  88256. }
  88257. /**
  88258. * @access protected
  88259. */
  88260. function validateChangelog()
  88261. {
  88262. return true;
  88263. }
  88264. /**
  88265. * @access protected
  88266. */
  88267. function validateFilelist()
  88268. {
  88269. return true;
  88270. }
  88271. /**
  88272. * @access protected
  88273. */
  88274. function validateDeps()
  88275. {
  88276. return true;
  88277. }
  88278. }<?php
  88279. /**
  88280. * Channel Validator for the pecl.php.net channel
  88281. *
  88282. * PHP 4 and PHP 5
  88283. *
  88284. * @category pear
  88285. * @package PEAR
  88286. * @author Greg Beaver <cellog@php.net>
  88287. * @copyright 1997-2006 The PHP Group
  88288. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  88289. * @link http://pear.php.net/package/PEAR
  88290. * @since File available since Release 1.4.0a5
  88291. */
  88292. /**
  88293. * This is the parent class for all validators
  88294. */
  88295. require_once 'phar://go-pear.phar/' . 'PEAR/Validate.php';
  88296. /**
  88297. * Channel Validator for the pecl.php.net channel
  88298. * @category pear
  88299. * @package PEAR
  88300. * @author Greg Beaver <cellog@php.net>
  88301. * @copyright 1997-2009 The Authors
  88302. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  88303. * @version Release: 1.10.16
  88304. * @link http://pear.php.net/package/PEAR
  88305. * @since Class available since Release 1.4.0a5
  88306. */
  88307. class PEAR_Validator_PECL extends PEAR_Validate
  88308. {
  88309. function validateVersion()
  88310. {
  88311. if ($this->_state == PEAR_VALIDATE_PACKAGING) {
  88312. $version = $this->_packagexml->getVersion();
  88313. $versioncomponents = explode('.', $version);
  88314. $last = array_pop($versioncomponents);
  88315. if (substr($last, 1, 2) == 'rc') {
  88316. $this->_addFailure('version', 'Release Candidate versions must have ' .
  88317. 'upper-case RC, not lower-case rc');
  88318. return false;
  88319. }
  88320. }
  88321. return true;
  88322. }
  88323. function validatePackageName()
  88324. {
  88325. $ret = parent::validatePackageName();
  88326. if ($this->_packagexml->getPackageType() == 'extsrc' ||
  88327. $this->_packagexml->getPackageType() == 'zendextsrc') {
  88328. if (strtolower($this->_packagexml->getPackage()) !=
  88329. strtolower($this->_packagexml->getProvidesExtension())) {
  88330. $this->_addWarning('providesextension', 'package name "' .
  88331. $this->_packagexml->getPackage() . '" is different from extension name "' .
  88332. $this->_packagexml->getProvidesExtension() . '"');
  88333. }
  88334. }
  88335. return $ret;
  88336. }
  88337. }
  88338. ?><?php
  88339. /**
  88340. * PEAR_XMLParser
  88341. *
  88342. * PHP versions 4 and 5
  88343. *
  88344. * @category pear
  88345. * @package PEAR
  88346. * @author Greg Beaver <cellog@php.net>
  88347. * @author Stephan Schmidt (original XML_Unserializer code)
  88348. * @copyright 1997-2009 The Authors
  88349. * @license http://opensource.org/licenses/bsd-license New BSD License
  88350. * @link http://pear.php.net/package/PEAR
  88351. * @since File available since Release 1.4.0a1
  88352. */
  88353. /**
  88354. * Parser for any xml file
  88355. * @category pear
  88356. * @package PEAR
  88357. * @author Greg Beaver <cellog@php.net>
  88358. * @author Stephan Schmidt (original XML_Unserializer code)
  88359. * @copyright 1997-2009 The Authors
  88360. * @license http://opensource.org/licenses/bsd-license New BSD License
  88361. * @version Release: 1.10.16
  88362. * @link http://pear.php.net/package/PEAR
  88363. * @since Class available since Release 1.4.0a1
  88364. */
  88365. class PEAR_XMLParser
  88366. {
  88367. /**
  88368. * unserilialized data
  88369. * @var string $_serializedData
  88370. */
  88371. var $_unserializedData = null;
  88372. /**
  88373. * name of the root tag
  88374. * @var string $_root
  88375. */
  88376. var $_root = null;
  88377. /**
  88378. * stack for all data that is found
  88379. * @var array $_dataStack
  88380. */
  88381. var $_dataStack = array();
  88382. /**
  88383. * stack for all values that are generated
  88384. * @var array $_valStack
  88385. */
  88386. var $_valStack = array();
  88387. /**
  88388. * current tag depth
  88389. * @var int $_depth
  88390. */
  88391. var $_depth = 0;
  88392. /**
  88393. * The XML encoding to use
  88394. * @var string $encoding
  88395. */
  88396. var $encoding = 'ISO-8859-1';
  88397. /**
  88398. * @return array
  88399. */
  88400. function getData()
  88401. {
  88402. return $this->_unserializedData;
  88403. }
  88404. /**
  88405. * @param string xml content
  88406. * @return true|PEAR_Error
  88407. */
  88408. function parse($data)
  88409. {
  88410. if (!extension_loaded('xml')) {
  88411. include_once 'phar://go-pear.phar/' . 'PEAR.php';
  88412. return PEAR::raiseError("XML Extension not found", 1);
  88413. }
  88414. $this->_dataStack = $this->_valStack = array();
  88415. $this->_depth = 0;
  88416. if (
  88417. strpos($data, 'encoding="UTF-8"')
  88418. || strpos($data, 'encoding="utf-8"')
  88419. || strpos($data, "encoding='UTF-8'")
  88420. || strpos($data, "encoding='utf-8'")
  88421. ) {
  88422. $this->encoding = 'UTF-8';
  88423. }
  88424. $xp = xml_parser_create($this->encoding);
  88425. xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, 0);
  88426. xml_set_object($xp, $this);
  88427. xml_set_element_handler($xp, 'startHandler', 'endHandler');
  88428. xml_set_character_data_handler($xp, 'cdataHandler');
  88429. if (!xml_parse($xp, $data)) {
  88430. $msg = xml_error_string(xml_get_error_code($xp));
  88431. $line = xml_get_current_line_number($xp);
  88432. xml_parser_free($xp);
  88433. include_once 'phar://go-pear.phar/' . 'PEAR.php';
  88434. return PEAR::raiseError("XML Error: '$msg' on line '$line'", 2);
  88435. }
  88436. xml_parser_free($xp);
  88437. return true;
  88438. }
  88439. /**
  88440. * Start element handler for XML parser
  88441. *
  88442. * @access private
  88443. * @param object $parser XML parser object
  88444. * @param string $element XML element
  88445. * @param array $attribs attributes of XML tag
  88446. * @return void
  88447. */
  88448. function startHandler($parser, $element, $attribs)
  88449. {
  88450. $this->_depth++;
  88451. $this->_dataStack[$this->_depth] = null;
  88452. $val = array(
  88453. 'name' => $element,
  88454. 'value' => null,
  88455. 'type' => 'string',
  88456. 'childrenKeys' => array(),
  88457. 'aggregKeys' => array()
  88458. );
  88459. if (count($attribs) > 0) {
  88460. $val['children'] = array();
  88461. $val['type'] = 'array';
  88462. $val['children']['attribs'] = $attribs;
  88463. }
  88464. array_push($this->_valStack, $val);
  88465. }
  88466. /**
  88467. * post-process data
  88468. *
  88469. * @param string $data
  88470. * @param string $element element name
  88471. */
  88472. function postProcess($data, $element)
  88473. {
  88474. return trim($data);
  88475. }
  88476. /**
  88477. * End element handler for XML parser
  88478. *
  88479. * @access private
  88480. * @param object XML parser object
  88481. * @param string
  88482. * @return void
  88483. */
  88484. function endHandler($parser, $element)
  88485. {
  88486. $value = array_pop($this->_valStack);
  88487. $data = $this->postProcess($this->_dataStack[$this->_depth], $element);
  88488. // adjust type of the value
  88489. switch (strtolower($value['type'])) {
  88490. // unserialize an array
  88491. case 'array':
  88492. if ($data !== '') {
  88493. $value['children']['_content'] = $data;
  88494. }
  88495. $value['value'] = isset($value['children']) ? $value['children'] : array();
  88496. break;
  88497. /*
  88498. * unserialize a null value
  88499. */
  88500. case 'null':
  88501. $data = null;
  88502. break;
  88503. /*
  88504. * unserialize any scalar value
  88505. */
  88506. default:
  88507. settype($data, $value['type']);
  88508. $value['value'] = $data;
  88509. break;
  88510. }
  88511. $parent = array_pop($this->_valStack);
  88512. if ($parent === null) {
  88513. $this->_unserializedData = &$value['value'];
  88514. $this->_root = &$value['name'];
  88515. return true;
  88516. }
  88517. // parent has to be an array
  88518. if (!isset($parent['children']) || !is_array($parent['children'])) {
  88519. $parent['children'] = array();
  88520. if ($parent['type'] != 'array') {
  88521. $parent['type'] = 'array';
  88522. }
  88523. }
  88524. if (!empty($value['name'])) {
  88525. // there already has been a tag with this name
  88526. if (in_array($value['name'], $parent['childrenKeys'])) {
  88527. // no aggregate has been created for this tag
  88528. if (!in_array($value['name'], $parent['aggregKeys'])) {
  88529. if (isset($parent['children'][$value['name']])) {
  88530. $parent['children'][$value['name']] = array($parent['children'][$value['name']]);
  88531. } else {
  88532. $parent['children'][$value['name']] = array();
  88533. }
  88534. array_push($parent['aggregKeys'], $value['name']);
  88535. }
  88536. array_push($parent['children'][$value['name']], $value['value']);
  88537. } else {
  88538. $parent['children'][$value['name']] = &$value['value'];
  88539. array_push($parent['childrenKeys'], $value['name']);
  88540. }
  88541. } else {
  88542. array_push($parent['children'],$value['value']);
  88543. }
  88544. array_push($this->_valStack, $parent);
  88545. $this->_depth--;
  88546. }
  88547. /**
  88548. * Handler for character data
  88549. *
  88550. * @access private
  88551. * @param object XML parser object
  88552. * @param string CDATA
  88553. * @return void
  88554. */
  88555. function cdataHandler($parser, $cdata)
  88556. {
  88557. $this->_dataStack[$this->_depth] .= $cdata;
  88558. }
  88559. }<?php
  88560. /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
  88561. // +-----------------------------------------------------------------------------+
  88562. // | Copyright (c) 2003 Sérgio Gonçalves Carvalho |
  88563. // +-----------------------------------------------------------------------------+
  88564. // | This file is part of Structures_Graph. |
  88565. // | |
  88566. // | Structures_Graph is free software; you can redistribute it and/or modify |
  88567. // | it under the terms of the GNU Lesser General Public License as published by |
  88568. // | the Free Software Foundation; either version 2.1 of the License, or |
  88569. // | (at your option) any later version. |
  88570. // | |
  88571. // | Structures_Graph is distributed in the hope that it will be useful, |
  88572. // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
  88573. // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
  88574. // | GNU Lesser General Public License for more details. |
  88575. // | |
  88576. // | You should have received a copy of the GNU Lesser General Public License |
  88577. // | along with Structures_Graph; if not, write to the Free Software |
  88578. // | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
  88579. // | 02111-1307 USA |
  88580. // +-----------------------------------------------------------------------------+
  88581. // | Author: Sérgio Carvalho <sergio.carvalho@portugalmail.com> |
  88582. // +-----------------------------------------------------------------------------+
  88583. //
  88584. /**
  88585. * The Graph.php file contains the definition of the Structures_Graph class
  88586. *
  88587. * @package Structures_Graph
  88588. */
  88589. /* dependencies {{{ */
  88590. require_once 'phar://go-pear.phar/' . 'PEAR.php';
  88591. require_once 'phar://go-pear.phar/' . 'Structures/Graph/Node.php';
  88592. /* }}} */
  88593. define('STRUCTURES_GRAPH_ERROR_GENERIC', 100);
  88594. /* class Structures_Graph {{{ */
  88595. /**
  88596. * The Structures_Graph class represents a graph data structure.
  88597. *
  88598. * A Graph is a data structure composed by a set of nodes, connected by arcs.
  88599. * Graphs may either be directed or undirected. In a directed graph, arcs are
  88600. * directional, and can be traveled only one way. In an undirected graph, arcs
  88601. * are bidirectional, and can be traveled both ways.
  88602. *
  88603. * @author Sérgio Carvalho <sergio.carvalho@portugalmail.com>
  88604. * @copyright (c) 2004 by Sérgio Carvalho
  88605. * @package Structures_Graph
  88606. */
  88607. /* }}} */
  88608. class Structures_Graph
  88609. {
  88610. /**
  88611. * List of node objects in this graph
  88612. * @access private
  88613. */
  88614. var $_nodes = array();
  88615. /**
  88616. * If the graph is directed or not
  88617. * @access private
  88618. */
  88619. var $_directed = false;
  88620. /**
  88621. * Constructor
  88622. *
  88623. * @param boolean $directed Set to true if the graph is directed.
  88624. * Set to false if it is not directed.
  88625. */
  88626. public function __construct($directed = true)
  88627. {
  88628. $this->_directed = $directed;
  88629. }
  88630. /**
  88631. * Old constructor (PHP4-style; kept for BC with extending classes)
  88632. *
  88633. * @param boolean $directed Set to true if the graph is directed.
  88634. * Set to false if it is not directed.
  88635. *
  88636. * @return void
  88637. */
  88638. public function Structures_Graph($directed = true)
  88639. {
  88640. $this->__construct($directed);
  88641. }
  88642. /**
  88643. * Return true if a graph is directed
  88644. *
  88645. * @return boolean true if the graph is directed
  88646. */
  88647. public function isDirected()
  88648. {
  88649. return (boolean) $this->_directed;
  88650. }
  88651. /**
  88652. * Add a Node to the Graph
  88653. *
  88654. * @param Structures_Graph_Node $newNode The node to be added.
  88655. *
  88656. * @return void
  88657. */
  88658. public function addNode(&$newNode)
  88659. {
  88660. // We only add nodes
  88661. if (!is_a($newNode, 'Structures_Graph_Node')) {
  88662. return Pear::raiseError(
  88663. 'Structures_Graph::addNode received an object that is not'
  88664. . ' a Structures_Graph_Node',
  88665. STRUCTURES_GRAPH_ERROR_GENERIC
  88666. );
  88667. }
  88668. //Graphs are node *sets*, so duplicates are forbidden.
  88669. // We allow nodes that are exactly equal, but disallow equal references.
  88670. foreach ($this->_nodes as $key => $node) {
  88671. /*
  88672. ZE1 equality operators choke on the recursive cycle introduced
  88673. by the _graph field in the Node object.
  88674. So, we'll check references the hard way
  88675. (change $this->_nodes[$key] and check if the change reflects in
  88676. $node)
  88677. */
  88678. $savedData = $this->_nodes[$key];
  88679. $referenceIsEqualFlag = false;
  88680. $this->_nodes[$key] = true;
  88681. if ($node === true) {
  88682. $this->_nodes[$key] = false;
  88683. if ($node === false) {
  88684. $referenceIsEqualFlag = true;
  88685. }
  88686. }
  88687. $this->_nodes[$key] = $savedData;
  88688. if ($referenceIsEqualFlag) {
  88689. return Pear::raiseError(
  88690. 'Structures_Graph::addNode received an object that is'
  88691. . ' a duplicate for this dataset',
  88692. STRUCTURES_GRAPH_ERROR_GENERIC
  88693. );
  88694. }
  88695. }
  88696. $this->_nodes[] =& $newNode;
  88697. $newNode->setGraph($this);
  88698. }
  88699. /**
  88700. * Remove a Node from the Graph
  88701. *
  88702. * @param Structures_Graph_Node $node The node to be removed from the graph
  88703. *
  88704. * @return void
  88705. * @todo This is unimplemented
  88706. */
  88707. public function removeNode(&$node)
  88708. {
  88709. }
  88710. /**
  88711. * Return the node set, in no particular order.
  88712. * For ordered node sets, use a Graph Manipulator insted.
  88713. *
  88714. * @return array The set of nodes in this graph
  88715. * @see Structures_Graph_Manipulator_TopologicalSorter
  88716. */
  88717. public function &getNodes()
  88718. {
  88719. return $this->_nodes;
  88720. }
  88721. }
  88722. ?>
  88723. <?php
  88724. /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
  88725. // +-----------------------------------------------------------------------------+
  88726. // | Copyright (c) 2003 Sérgio Gonçalves Carvalho |
  88727. // +-----------------------------------------------------------------------------+
  88728. // | This file is part of Structures_Graph. |
  88729. // | |
  88730. // | Structures_Graph is free software; you can redistribute it and/or modify |
  88731. // | it under the terms of the GNU Lesser General Public License as published by |
  88732. // | the Free Software Foundation; either version 2.1 of the License, or |
  88733. // | (at your option) any later version. |
  88734. // | |
  88735. // | Structures_Graph is distributed in the hope that it will be useful, |
  88736. // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
  88737. // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
  88738. // | GNU Lesser General Public License for more details. |
  88739. // | |
  88740. // | You should have received a copy of the GNU Lesser General Public License |
  88741. // | along with Structures_Graph; if not, write to the Free Software |
  88742. // | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
  88743. // | 02111-1307 USA |
  88744. // +-----------------------------------------------------------------------------+
  88745. // | Author: Sérgio Carvalho <sergio.carvalho@portugalmail.com> |
  88746. // +-----------------------------------------------------------------------------+
  88747. //
  88748. /**
  88749. * This file contains the definition of the Structures_Graph_Manipulator_AcyclicTest graph manipulator.
  88750. *
  88751. * @see Structures_Graph_Manipulator_AcyclicTest
  88752. * @package Structures_Graph
  88753. */
  88754. /* dependencies {{{ */
  88755. /** */
  88756. require_once 'phar://go-pear.phar/' . 'PEAR.php';
  88757. /** */
  88758. require_once 'phar://go-pear.phar/' . 'Structures/Graph.php';
  88759. /** */
  88760. require_once 'phar://go-pear.phar/' . 'Structures/Graph/Node.php';
  88761. /* }}} */
  88762. /* class Structures_Graph_Manipulator_AcyclicTest {{{ */
  88763. /**
  88764. * The Structures_Graph_Manipulator_AcyclicTest is a graph manipulator
  88765. * which tests whether a graph contains a cycle.
  88766. *
  88767. * The definition of an acyclic graph used in this manipulator is that of a
  88768. * DAG. The graph must be directed, or else it is considered cyclic, even when
  88769. * there are no arcs.
  88770. *
  88771. * @author Sérgio Carvalho <sergio.carvalho@portugalmail.com>
  88772. * @copyright (c) 2004 by Sérgio Carvalho
  88773. * @package Structures_Graph
  88774. */
  88775. class Structures_Graph_Manipulator_AcyclicTest {
  88776. /* _nonVisitedInDegree {{{ */
  88777. /**
  88778. *
  88779. * This is a variant of Structures_Graph::inDegree which does
  88780. * not count nodes marked as visited.
  88781. *
  88782. * @return integer Number of non-visited nodes that link to this one
  88783. */
  88784. protected static function _nonVisitedInDegree(&$node) {
  88785. $result = 0;
  88786. $graphNodes =& $node->_graph->getNodes();
  88787. foreach (array_keys($graphNodes) as $key) {
  88788. if ((!$graphNodes[$key]->getMetadata('acyclic-test-visited')) && $graphNodes[$key]->connectsTo($node)) $result++;
  88789. }
  88790. return $result;
  88791. }
  88792. /* }}} */
  88793. /* _isAcyclic {{{ */
  88794. /**
  88795. * Check if the graph is acyclic
  88796. */
  88797. protected static function _isAcyclic(&$graph) {
  88798. // Mark every node as not visited
  88799. $nodes =& $graph->getNodes();
  88800. $nodeKeys = array_keys($nodes);
  88801. $refGenerator = array();
  88802. foreach($nodeKeys as $key) {
  88803. $refGenerator[] = false;
  88804. $nodes[$key]->setMetadata('acyclic-test-visited', $refGenerator[sizeof($refGenerator) - 1]);
  88805. }
  88806. // Iteratively peel off leaf nodes
  88807. do {
  88808. // Find out which nodes are leafs (excluding visited nodes)
  88809. $leafNodes = array();
  88810. foreach($nodeKeys as $key) {
  88811. if ((!$nodes[$key]->getMetadata('acyclic-test-visited')) && Structures_Graph_Manipulator_AcyclicTest::_nonVisitedInDegree($nodes[$key]) == 0) {
  88812. $leafNodes[] =& $nodes[$key];
  88813. }
  88814. }
  88815. // Mark leafs as visited
  88816. for ($i=sizeof($leafNodes) - 1; $i>=0; $i--) {
  88817. $visited =& $leafNodes[$i]->getMetadata('acyclic-test-visited');
  88818. $visited = true;
  88819. $leafNodes[$i]->setMetadata('acyclic-test-visited', $visited);
  88820. }
  88821. } while (sizeof($leafNodes) > 0);
  88822. // If graph is a DAG, there should be no non-visited nodes. Let's try to prove otherwise
  88823. $result = true;
  88824. foreach($nodeKeys as $key) if (!$nodes[$key]->getMetadata('acyclic-test-visited')) $result = false;
  88825. // Cleanup visited marks
  88826. foreach($nodeKeys as $key) $nodes[$key]->unsetMetadata('acyclic-test-visited');
  88827. return $result;
  88828. }
  88829. /* }}} */
  88830. /* isAcyclic {{{ */
  88831. /**
  88832. *
  88833. * isAcyclic returns true if a graph contains no cycles, false otherwise.
  88834. *
  88835. * @return boolean true iff graph is acyclic
  88836. */
  88837. public static function isAcyclic(&$graph) {
  88838. // We only test graphs
  88839. if (!is_a($graph, 'Structures_Graph')) return Pear::raiseError('Structures_Graph_Manipulator_AcyclicTest::isAcyclic received an object that is not a Structures_Graph', STRUCTURES_GRAPH_ERROR_GENERIC);
  88840. if (!$graph->isDirected()) return false; // Only directed graphs may be acyclic
  88841. return Structures_Graph_Manipulator_AcyclicTest::_isAcyclic($graph);
  88842. }
  88843. /* }}} */
  88844. }
  88845. /* }}} */
  88846. ?>
  88847. <?php
  88848. /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
  88849. // +-----------------------------------------------------------------------------+
  88850. // | Copyright (c) 2003 Sérgio Gonçalves Carvalho |
  88851. // +-----------------------------------------------------------------------------+
  88852. // | This file is part of Structures_Graph. |
  88853. // | |
  88854. // | Structures_Graph is free software; you can redistribute it and/or modify |
  88855. // | it under the terms of the GNU Lesser General Public License as published by |
  88856. // | the Free Software Foundation; either version 2.1 of the License, or |
  88857. // | (at your option) any later version. |
  88858. // | |
  88859. // | Structures_Graph is distributed in the hope that it will be useful, |
  88860. // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
  88861. // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
  88862. // | GNU Lesser General Public License for more details. |
  88863. // | |
  88864. // | You should have received a copy of the GNU Lesser General Public License |
  88865. // | along with Structures_Graph; if not, write to the Free Software |
  88866. // | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
  88867. // | 02111-1307 USA |
  88868. // +-----------------------------------------------------------------------------+
  88869. // | Author: Sérgio Carvalho <sergio.carvalho@portugalmail.com> |
  88870. // +-----------------------------------------------------------------------------+
  88871. //
  88872. /**
  88873. * This file contains the definition of the Structures_Graph_Manipulator_TopologicalSorter class.
  88874. *
  88875. * @package Structures_Graph
  88876. */
  88877. require_once 'phar://go-pear.phar/' . 'PEAR.php';
  88878. require_once 'phar://go-pear.phar/' . 'Structures/Graph.php';
  88879. require_once 'phar://go-pear.phar/' . 'Structures/Graph/Node.php';
  88880. require_once 'phar://go-pear.phar/' . 'Structures/Graph/Manipulator/AcyclicTest.php';
  88881. /**
  88882. * The Structures_Graph_Manipulator_TopologicalSorter is a manipulator
  88883. * which is able to return the set of nodes in a graph, sorted by topological
  88884. * order.
  88885. *
  88886. * A graph may only be sorted topologically iff it's a DAG. You can test it
  88887. * with the Structures_Graph_Manipulator_AcyclicTest.
  88888. *
  88889. * @author Sérgio Carvalho <sergio.carvalho@portugalmail.com>
  88890. * @copyright (c) 2004 by Sérgio Carvalho
  88891. * @see Structures_Graph_Manipulator_AcyclicTest
  88892. * @package Structures_Graph
  88893. */
  88894. class Structures_Graph_Manipulator_TopologicalSorter
  88895. {
  88896. /**
  88897. * This is a variant of Structures_Graph::inDegree which does
  88898. * not count nodes marked as visited.
  88899. *
  88900. * @param object $node Node to check
  88901. *
  88902. * @return integer Number of non-visited nodes that link to this one
  88903. */
  88904. protected static function _nonVisitedInDegree(&$node)
  88905. {
  88906. $result = 0;
  88907. $graphNodes =& $node->_graph->getNodes();
  88908. foreach (array_keys($graphNodes) as $key) {
  88909. if ((!$graphNodes[$key]->getMetadata('topological-sort-visited'))
  88910. && $graphNodes[$key]->connectsTo($node)
  88911. ) {
  88912. $result++;
  88913. }
  88914. }
  88915. return $result;
  88916. }
  88917. /**
  88918. * Sort implementation
  88919. *
  88920. * @param object $graph Graph to sort
  88921. *
  88922. * @return void
  88923. */
  88924. protected static function _sort(&$graph)
  88925. {
  88926. // Mark every node as not visited
  88927. $nodes =& $graph->getNodes();
  88928. $nodeKeys = array_keys($nodes);
  88929. $refGenerator = array();
  88930. foreach ($nodeKeys as $key) {
  88931. $refGenerator[] = false;
  88932. $nodes[$key]->setMetadata(
  88933. 'topological-sort-visited',
  88934. $refGenerator[sizeof($refGenerator) - 1]
  88935. );
  88936. }
  88937. // Iteratively peel off leaf nodes
  88938. $topologicalLevel = 0;
  88939. do {
  88940. // Find out which nodes are leafs (excluding visited nodes)
  88941. $leafNodes = array();
  88942. foreach ($nodeKeys as $key) {
  88943. if ((!$nodes[$key]->getMetadata('topological-sort-visited'))
  88944. && static::_nonVisitedInDegree($nodes[$key]) == 0
  88945. ) {
  88946. $leafNodes[] =& $nodes[$key];
  88947. }
  88948. }
  88949. // Mark leafs as visited
  88950. $refGenerator[] = $topologicalLevel;
  88951. for ($i = sizeof($leafNodes) - 1; $i>=0; $i--) {
  88952. $visited =& $leafNodes[$i]->getMetadata('topological-sort-visited');
  88953. $visited = true;
  88954. $leafNodes[$i]->setMetadata('topological-sort-visited', $visited);
  88955. $leafNodes[$i]->setMetadata(
  88956. 'topological-sort-level',
  88957. $refGenerator[sizeof($refGenerator) - 1]
  88958. );
  88959. }
  88960. $topologicalLevel++;
  88961. } while (sizeof($leafNodes) > 0);
  88962. // Cleanup visited marks
  88963. foreach ($nodeKeys as $key) {
  88964. $nodes[$key]->unsetMetadata('topological-sort-visited');
  88965. }
  88966. }
  88967. /**
  88968. * Sort returns the graph's nodes, sorted by topological order.
  88969. *
  88970. * The result is an array with as many entries as topological levels.
  88971. * Each entry in this array is an array of nodes within
  88972. * the given topological level.
  88973. *
  88974. * @param object $graph Graph to sort
  88975. *
  88976. * @return array The graph's nodes, sorted by topological order.
  88977. */
  88978. public static function sort(&$graph)
  88979. {
  88980. // We only sort graphs
  88981. if (!is_a($graph, 'Structures_Graph')) {
  88982. return Pear::raiseError(
  88983. 'Structures_Graph_Manipulator_TopologicalSorter::sort received'
  88984. . ' an object that is not a Structures_Graph',
  88985. STRUCTURES_GRAPH_ERROR_GENERIC
  88986. );
  88987. }
  88988. if (!Structures_Graph_Manipulator_AcyclicTest::isAcyclic($graph)) {
  88989. return Pear::raiseError(
  88990. 'Structures_Graph_Manipulator_TopologicalSorter::sort'
  88991. . ' received an graph that has cycles',
  88992. STRUCTURES_GRAPH_ERROR_GENERIC
  88993. );
  88994. }
  88995. Structures_Graph_Manipulator_TopologicalSorter::_sort($graph);
  88996. $result = array();
  88997. // Fill out result array
  88998. $nodes =& $graph->getNodes();
  88999. $nodeKeys = array_keys($nodes);
  89000. foreach ($nodeKeys as $key) {
  89001. if (!array_key_exists($nodes[$key]->getMetadata('topological-sort-level'), $result)) {
  89002. $result[$nodes[$key]->getMetadata('topological-sort-level')]
  89003. = array();
  89004. }
  89005. $result[$nodes[$key]->getMetadata('topological-sort-level')][]
  89006. =& $nodes[$key];
  89007. $nodes[$key]->unsetMetadata('topological-sort-level');
  89008. }
  89009. return $result;
  89010. }
  89011. }
  89012. ?>
  89013. <?php
  89014. /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
  89015. // +-----------------------------------------------------------------------------+
  89016. // | Copyright (c) 2003 Sérgio Gonçalves Carvalho |
  89017. // +-----------------------------------------------------------------------------+
  89018. // | This file is part of Structures_Graph. |
  89019. // | |
  89020. // | Structures_Graph is free software; you can redistribute it and/or modify |
  89021. // | it under the terms of the GNU Lesser General Public License as published by |
  89022. // | the Free Software Foundation; either version 2.1 of the License, or |
  89023. // | (at your option) any later version. |
  89024. // | |
  89025. // | Structures_Graph is distributed in the hope that it will be useful, |
  89026. // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
  89027. // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
  89028. // | GNU Lesser General Public License for more details. |
  89029. // | |
  89030. // | You should have received a copy of the GNU Lesser General Public License |
  89031. // | along with Structures_Graph; if not, write to the Free Software |
  89032. // | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
  89033. // | 02111-1307 USA |
  89034. // +-----------------------------------------------------------------------------+
  89035. // | Author: Sérgio Carvalho <sergio.carvalho@portugalmail.com> |
  89036. // +-----------------------------------------------------------------------------+
  89037. //
  89038. /**
  89039. * This file contains the definition of the Structures_Graph_Node class
  89040. *
  89041. * @see Structures_Graph_Node
  89042. * @package Structures_Graph
  89043. */
  89044. /* dependencies {{{ */
  89045. /** */
  89046. require_once 'phar://go-pear.phar/' . 'PEAR.php';
  89047. /** */
  89048. require_once 'phar://go-pear.phar/' . 'Structures/Graph.php';
  89049. /* }}} */
  89050. /* class Structures_Graph_Node {{{ */
  89051. /**
  89052. * The Structures_Graph_Node class represents a Node that can be member of a
  89053. * graph node set.
  89054. *
  89055. * A graph node can contain data. Under this API, the node contains default data,
  89056. * and key index data. It behaves, thus, both as a regular data node, and as a
  89057. * dictionary (or associative array) node.
  89058. *
  89059. * Regular data is accessed via getData and setData. Key indexed data is accessed
  89060. * via getMetadata and setMetadata.
  89061. *
  89062. * @author Sérgio Carvalho <sergio.carvalho@portugalmail.com>
  89063. * @copyright (c) 2004 by Sérgio Carvalho
  89064. * @package Structures_Graph
  89065. */
  89066. /* }}} */
  89067. class Structures_Graph_Node {
  89068. /* fields {{{ */
  89069. /**
  89070. * @access private
  89071. */
  89072. var $_data = null;
  89073. /** @access private */
  89074. var $_metadata = array();
  89075. /** @access private */
  89076. var $_arcs = array();
  89077. /** @access private */
  89078. var $_graph = null;
  89079. /* }}} */
  89080. /* Constructor {{{ */
  89081. /**
  89082. *
  89083. * Constructor
  89084. *
  89085. * @access public
  89086. */
  89087. function __construct() {
  89088. }
  89089. /* }}} */
  89090. /* getGraph {{{ */
  89091. /**
  89092. *
  89093. * Node graph getter
  89094. *
  89095. * @return Structures_Graph Graph where node is stored
  89096. * @access public
  89097. */
  89098. function &getGraph() {
  89099. return $this->_graph;
  89100. }
  89101. /* }}} */
  89102. /* setGraph {{{ */
  89103. /**
  89104. *
  89105. * Node graph setter. This method should not be called directly. Use Graph::addNode instead.
  89106. *
  89107. * @param Structures_Graph Set the graph for this node.
  89108. * @see Structures_Graph::addNode()
  89109. * @access public
  89110. */
  89111. function setGraph(&$graph) {
  89112. $this->_graph =& $graph;
  89113. }
  89114. /* }}} */
  89115. /* getData {{{ */
  89116. /**
  89117. *
  89118. * Node data getter.
  89119. *
  89120. * Each graph node can contain a reference to one variable. This is the getter for that reference.
  89121. *
  89122. * @return mixed Data stored in node
  89123. * @access public
  89124. */
  89125. function &getData() {
  89126. return $this->_data;
  89127. }
  89128. /* }}} */
  89129. /* setData {{{ */
  89130. /**
  89131. *
  89132. * Node data setter
  89133. *
  89134. * Each graph node can contain a reference to one variable. This is the setter for that reference.
  89135. *
  89136. * @return mixed Data to store in node
  89137. * @access public
  89138. */
  89139. function setData(&$data) {
  89140. $this->_data =& $data;
  89141. }
  89142. /* }}} */
  89143. /* metadataKeyExists {{{ */
  89144. /**
  89145. *
  89146. * Test for existence of metadata under a given key.
  89147. *
  89148. * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an
  89149. * associative array or in a dictionary. This method tests whether a given metadata key exists for this node.
  89150. *
  89151. * @param string Key to test
  89152. * @return boolean
  89153. * @access public
  89154. */
  89155. function metadataKeyExists($key) {
  89156. return array_key_exists($key, $this->_metadata);
  89157. }
  89158. /* }}} */
  89159. /* getMetadata {{{ */
  89160. /**
  89161. *
  89162. * Node metadata getter
  89163. *
  89164. * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an
  89165. * associative array or in a dictionary. This method gets the data under the given key. If the key does
  89166. * not exist, an error will be thrown, so testing using metadataKeyExists might be needed.
  89167. *
  89168. * @param string Key
  89169. * @param boolean nullIfNonexistent (defaults to false).
  89170. * @return mixed Metadata Data stored in node under given key
  89171. * @see metadataKeyExists
  89172. * @access public
  89173. */
  89174. function &getMetadata($key, $nullIfNonexistent = false) {
  89175. if (array_key_exists($key, $this->_metadata)) {
  89176. return $this->_metadata[$key];
  89177. } else {
  89178. if ($nullIfNonexistent) {
  89179. $a = null;
  89180. return $a;
  89181. } else {
  89182. $a = Pear::raiseError('Structures_Graph_Node::getMetadata: Requested key does not exist', STRUCTURES_GRAPH_ERROR_GENERIC);
  89183. return $a;
  89184. }
  89185. }
  89186. }
  89187. /* }}} */
  89188. /* unsetMetadata {{{ */
  89189. /**
  89190. *
  89191. * Delete metadata by key
  89192. *
  89193. * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an
  89194. * associative array or in a dictionary. This method removes any data that might be stored under the provided key.
  89195. * If the key does not exist, no error is thrown, so it is safe using this method without testing for key existence.
  89196. *
  89197. * @param string Key
  89198. * @access public
  89199. */
  89200. function unsetMetadata($key) {
  89201. if (array_key_exists($key, $this->_metadata)) unset($this->_metadata[$key]);
  89202. }
  89203. /* }}} */
  89204. /* setMetadata {{{ */
  89205. /**
  89206. *
  89207. * Node metadata setter
  89208. *
  89209. * Each graph node can contain multiple 'metadata' entries, each stored under a different key, as in an
  89210. * associative array or in a dictionary. This method stores data under the given key. If the key already exists,
  89211. * previously stored data is discarded.
  89212. *
  89213. * @param string Key
  89214. * @param mixed Data
  89215. * @access public
  89216. */
  89217. function setMetadata($key, &$data) {
  89218. $this->_metadata[$key] =& $data;
  89219. }
  89220. /* }}} */
  89221. /* _connectTo {{{ */
  89222. /** @access private */
  89223. function _connectTo(&$destinationNode) {
  89224. $this->_arcs[] =& $destinationNode;
  89225. }
  89226. /* }}} */
  89227. /* connectTo {{{ */
  89228. /**
  89229. *
  89230. * Connect this node to another one.
  89231. *
  89232. * If the graph is not directed, the reverse arc, connecting $destinationNode to $this is also created.
  89233. *
  89234. * @param Structures_Graph_Node Node to connect to
  89235. * @access public
  89236. */
  89237. function connectTo(&$destinationNode) {
  89238. // We only connect to nodes
  89239. if (!is_a($destinationNode, 'Structures_Graph_Node')) return Pear::raiseError('Structures_Graph_Node::connectTo received an object that is not a Structures_Graph_Node', STRUCTURES_GRAPH_ERROR_GENERIC);
  89240. // Nodes must already be in graphs to be connected
  89241. if ($this->_graph == null) return Pear::raiseError('Structures_Graph_Node::connectTo Tried to connect a node that is not in a graph', STRUCTURES_GRAPH_ERROR_GENERIC);
  89242. if ($destinationNode->getGraph() == null) return Pear::raiseError('Structures_Graph_Node::connectTo Tried to connect to a node that is not in a graph', STRUCTURES_GRAPH_ERROR_GENERIC);
  89243. // Connect here
  89244. $this->_connectTo($destinationNode);
  89245. // If graph is undirected, connect back
  89246. if (!$this->_graph->isDirected()) {
  89247. $destinationNode->_connectTo($this);
  89248. }
  89249. }
  89250. /* }}} */
  89251. /* getNeighbours {{{ */
  89252. /**
  89253. *
  89254. * Return nodes connected to this one.
  89255. *
  89256. * @return array Array of nodes
  89257. * @access public
  89258. */
  89259. function getNeighbours() {
  89260. return $this->_arcs;
  89261. }
  89262. /* }}} */
  89263. /* connectsTo {{{ */
  89264. /**
  89265. *
  89266. * Test wether this node has an arc to the target node
  89267. *
  89268. * @return boolean True if the two nodes are connected
  89269. * @access public
  89270. */
  89271. function connectsTo(&$target) {
  89272. if (version_compare(PHP_VERSION, '5.0.0') >= 0) {
  89273. return in_array($target, $this->getNeighbours(), true);
  89274. }
  89275. $copy = $target;
  89276. $arcKeys = array_keys($this->_arcs);
  89277. foreach($arcKeys as $key) {
  89278. /* ZE1 chokes on this expression:
  89279. if ($target === $arc) return true;
  89280. so, we'll use more convoluted stuff
  89281. */
  89282. $arc =& $this->_arcs[$key];
  89283. $target = true;
  89284. if ($arc === true) {
  89285. $target = false;
  89286. if ($arc === false) {
  89287. $target = $copy;
  89288. return true;
  89289. }
  89290. }
  89291. }
  89292. $target = $copy;
  89293. return false;
  89294. }
  89295. /* }}} */
  89296. /* inDegree {{{ */
  89297. /**
  89298. *
  89299. * Calculate the in degree of the node.
  89300. *
  89301. * The indegree for a node is the number of arcs entering the node. For non directed graphs,
  89302. * the indegree is equal to the outdegree.
  89303. *
  89304. * @return integer In degree of the node
  89305. * @access public
  89306. */
  89307. function inDegree() {
  89308. if ($this->_graph == null) return 0;
  89309. if (!$this->_graph->isDirected()) return $this->outDegree();
  89310. $result = 0;
  89311. $graphNodes =& $this->_graph->getNodes();
  89312. foreach (array_keys($graphNodes) as $key) {
  89313. if ($graphNodes[$key]->connectsTo($this)) $result++;
  89314. }
  89315. return $result;
  89316. }
  89317. /* }}} */
  89318. /* outDegree {{{ */
  89319. /**
  89320. *
  89321. * Calculate the out degree of the node.
  89322. *
  89323. * The outdegree for a node is the number of arcs exiting the node. For non directed graphs,
  89324. * the outdegree is always equal to the indegree.
  89325. *
  89326. * @return integer Out degree of the node
  89327. * @access public
  89328. */
  89329. function outDegree() {
  89330. if ($this->_graph == null) return 0;
  89331. return sizeof($this->_arcs);
  89332. }
  89333. /* }}} */
  89334. }
  89335. ?>
  89336. <?php
  89337. /**
  89338. * File/Directory manipulation
  89339. *
  89340. * PHP versions 4 and 5
  89341. *
  89342. * @category pear
  89343. * @package System
  89344. * @author Tomas V.V.Cox <cox@idecnet.com>
  89345. * @copyright 1997-2009 The Authors
  89346. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  89347. * @link http://pear.php.net/package/PEAR
  89348. * @since File available since Release 0.1
  89349. */
  89350. /**
  89351. * base class
  89352. */
  89353. require_once 'phar://go-pear.phar/' . 'PEAR.php';
  89354. require_once 'phar://go-pear.phar/' . 'Console/Getopt.php';
  89355. $GLOBALS['_System_temp_files'] = array();
  89356. /**
  89357. * System offers cross platform compatible system functions
  89358. *
  89359. * Static functions for different operations. Should work under
  89360. * Unix and Windows. The names and usage has been taken from its respectively
  89361. * GNU commands. The functions will return (bool) false on error and will
  89362. * trigger the error with the PHP trigger_error() function (you can silence
  89363. * the error by prefixing a '@' sign after the function call, but this
  89364. * is not recommended practice. Instead use an error handler with
  89365. * {@link set_error_handler()}).
  89366. *
  89367. * Documentation on this class you can find in:
  89368. * http://pear.php.net/manual/
  89369. *
  89370. * Example usage:
  89371. * if (!@System::rm('-r file1 dir1')) {
  89372. * print "could not delete file1 or dir1";
  89373. * }
  89374. *
  89375. * In case you need to to pass file names with spaces,
  89376. * pass the params as an array:
  89377. *
  89378. * System::rm(array('-r', $file1, $dir1));
  89379. *
  89380. * @category pear
  89381. * @package System
  89382. * @author Tomas V.V. Cox <cox@idecnet.com>
  89383. * @copyright 1997-2006 The PHP Group
  89384. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  89385. * @version Release: 1.10.16
  89386. * @link http://pear.php.net/package/PEAR
  89387. * @since Class available since Release 0.1
  89388. * @static
  89389. */
  89390. class System
  89391. {
  89392. /**
  89393. * returns the commandline arguments of a function
  89394. *
  89395. * @param string $argv the commandline
  89396. * @param string $short_options the allowed option short-tags
  89397. * @param string $long_options the allowed option long-tags
  89398. * @return array the given options and there values
  89399. */
  89400. public static function _parseArgs($argv, $short_options, $long_options = null)
  89401. {
  89402. if (!is_array($argv) && $argv !== null) {
  89403. /*
  89404. // Quote all items that are a short option
  89405. $av = preg_split('/(\A| )--?[a-z0-9]+[ =]?((?<!\\\\)((,\s*)|((?<!,)\s+))?)/i', $argv, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE);
  89406. $offset = 0;
  89407. foreach ($av as $a) {
  89408. $b = trim($a[0]);
  89409. if ($b[0] == '"' || $b[0] == "'") {
  89410. continue;
  89411. }
  89412. $escape = escapeshellarg($b);
  89413. $pos = $a[1] + $offset;
  89414. $argv = substr_replace($argv, $escape, $pos, strlen($b));
  89415. $offset += 2;
  89416. }
  89417. */
  89418. // Find all items, quoted or otherwise
  89419. preg_match_all("/(?:[\"'])(.*?)(?:['\"])|([^\s]+)/", $argv, $av);
  89420. $argv = $av[1];
  89421. foreach ($av[2] as $k => $a) {
  89422. if (empty($a)) {
  89423. continue;
  89424. }
  89425. $argv[$k] = trim($a) ;
  89426. }
  89427. }
  89428. return Console_Getopt::getopt2($argv, $short_options, $long_options);
  89429. }
  89430. /**
  89431. * Output errors with PHP trigger_error(). You can silence the errors
  89432. * with prefixing a "@" sign to the function call: @System::mkdir(..);
  89433. *
  89434. * @param mixed $error a PEAR error or a string with the error message
  89435. * @return bool false
  89436. */
  89437. protected static function raiseError($error)
  89438. {
  89439. if (PEAR::isError($error)) {
  89440. $error = $error->getMessage();
  89441. }
  89442. trigger_error($error, E_USER_WARNING);
  89443. return false;
  89444. }
  89445. /**
  89446. * Creates a nested array representing the structure of a directory
  89447. *
  89448. * System::_dirToStruct('dir1', 0) =>
  89449. * Array
  89450. * (
  89451. * [dirs] => Array
  89452. * (
  89453. * [0] => dir1
  89454. * )
  89455. *
  89456. * [files] => Array
  89457. * (
  89458. * [0] => dir1/file2
  89459. * [1] => dir1/file3
  89460. * )
  89461. * )
  89462. * @param string $sPath Name of the directory
  89463. * @param integer $maxinst max. deep of the lookup
  89464. * @param integer $aktinst starting deep of the lookup
  89465. * @param bool $silent if true, do not emit errors.
  89466. * @return array the structure of the dir
  89467. */
  89468. protected static function _dirToStruct($sPath, $maxinst, $aktinst = 0, $silent = false)
  89469. {
  89470. $struct = array('dirs' => array(), 'files' => array());
  89471. if (($dir = @opendir($sPath)) === false) {
  89472. if (!$silent) {
  89473. System::raiseError("Could not open dir $sPath");
  89474. }
  89475. return $struct; // XXX could not open error
  89476. }
  89477. $struct['dirs'][] = $sPath = realpath($sPath); // XXX don't add if '.' or '..' ?
  89478. $list = array();
  89479. while (false !== ($file = readdir($dir))) {
  89480. if ($file != '.' && $file != '..') {
  89481. $list[] = $file;
  89482. }
  89483. }
  89484. closedir($dir);
  89485. natsort($list);
  89486. if ($aktinst < $maxinst || $maxinst == 0) {
  89487. foreach ($list as $val) {
  89488. $path = $sPath . DIRECTORY_SEPARATOR . $val;
  89489. if (is_dir($path) && !is_link($path)) {
  89490. $tmp = System::_dirToStruct($path, $maxinst, $aktinst+1, $silent);
  89491. $struct = array_merge_recursive($struct, $tmp);
  89492. } else {
  89493. $struct['files'][] = $path;
  89494. }
  89495. }
  89496. }
  89497. return $struct;
  89498. }
  89499. /**
  89500. * Creates a nested array representing the structure of a directory and files
  89501. *
  89502. * @param array $files Array listing files and dirs
  89503. * @return array
  89504. * @static
  89505. * @see System::_dirToStruct()
  89506. */
  89507. protected static function _multipleToStruct($files)
  89508. {
  89509. $struct = array('dirs' => array(), 'files' => array());
  89510. settype($files, 'array');
  89511. foreach ($files as $file) {
  89512. if (is_dir($file) && !is_link($file)) {
  89513. $tmp = System::_dirToStruct($file, 0);
  89514. $struct = array_merge_recursive($tmp, $struct);
  89515. } else {
  89516. if (!in_array($file, $struct['files'])) {
  89517. $struct['files'][] = $file;
  89518. }
  89519. }
  89520. }
  89521. return $struct;
  89522. }
  89523. /**
  89524. * The rm command for removing files.
  89525. * Supports multiple files and dirs and also recursive deletes
  89526. *
  89527. * @param string $args the arguments for rm
  89528. * @return mixed PEAR_Error or true for success
  89529. * @static
  89530. * @access public
  89531. */
  89532. public static function rm($args)
  89533. {
  89534. $opts = System::_parseArgs($args, 'rf'); // "f" does nothing but I like it :-)
  89535. if (PEAR::isError($opts)) {
  89536. return System::raiseError($opts);
  89537. }
  89538. foreach ($opts[0] as $opt) {
  89539. if ($opt[0] == 'r') {
  89540. $do_recursive = true;
  89541. }
  89542. }
  89543. $ret = true;
  89544. if (isset($do_recursive)) {
  89545. $struct = System::_multipleToStruct($opts[1]);
  89546. foreach ($struct['files'] as $file) {
  89547. if (!@unlink($file)) {
  89548. $ret = false;
  89549. }
  89550. }
  89551. rsort($struct['dirs']);
  89552. foreach ($struct['dirs'] as $dir) {
  89553. if (!@rmdir($dir)) {
  89554. $ret = false;
  89555. }
  89556. }
  89557. } else {
  89558. foreach ($opts[1] as $file) {
  89559. $delete = (is_dir($file)) ? 'rmdir' : 'unlink';
  89560. if (!@$delete($file)) {
  89561. $ret = false;
  89562. }
  89563. }
  89564. }
  89565. return $ret;
  89566. }
  89567. /**
  89568. * Make directories.
  89569. *
  89570. * The -p option will create parent directories
  89571. * @param string $args the name of the director(y|ies) to create
  89572. * @return bool True for success
  89573. */
  89574. public static function mkDir($args)
  89575. {
  89576. $opts = System::_parseArgs($args, 'pm:');
  89577. if (PEAR::isError($opts)) {
  89578. return System::raiseError($opts);
  89579. }
  89580. $mode = 0777; // default mode
  89581. foreach ($opts[0] as $opt) {
  89582. if ($opt[0] == 'p') {
  89583. $create_parents = true;
  89584. } elseif ($opt[0] == 'm') {
  89585. // if the mode is clearly an octal number (starts with 0)
  89586. // convert it to decimal
  89587. if (strlen($opt[1]) && $opt[1][0] == '0') {
  89588. $opt[1] = octdec($opt[1]);
  89589. } else {
  89590. // convert to int
  89591. $opt[1] += 0;
  89592. }
  89593. $mode = $opt[1];
  89594. }
  89595. }
  89596. $ret = true;
  89597. if (isset($create_parents)) {
  89598. foreach ($opts[1] as $dir) {
  89599. $dirstack = array();
  89600. while ((!file_exists($dir) || !is_dir($dir)) &&
  89601. $dir != DIRECTORY_SEPARATOR) {
  89602. array_unshift($dirstack, $dir);
  89603. $dir = dirname($dir);
  89604. }
  89605. while ($newdir = array_shift($dirstack)) {
  89606. if (!is_writeable(dirname($newdir))) {
  89607. $ret = false;
  89608. break;
  89609. }
  89610. if (!mkdir($newdir, $mode)) {
  89611. $ret = false;
  89612. }
  89613. }
  89614. }
  89615. } else {
  89616. foreach($opts[1] as $dir) {
  89617. if ((@file_exists($dir) || !is_dir($dir)) && !mkdir($dir, $mode)) {
  89618. $ret = false;
  89619. }
  89620. }
  89621. }
  89622. return $ret;
  89623. }
  89624. /**
  89625. * Concatenate files
  89626. *
  89627. * Usage:
  89628. * 1) $var = System::cat('sample.txt test.txt');
  89629. * 2) System::cat('sample.txt test.txt > final.txt');
  89630. * 3) System::cat('sample.txt test.txt >> final.txt');
  89631. *
  89632. * Note: as the class use fopen, urls should work also
  89633. *
  89634. * @param string $args the arguments
  89635. * @return boolean true on success
  89636. */
  89637. public static function &cat($args)
  89638. {
  89639. $ret = null;
  89640. $files = array();
  89641. if (!is_array($args)) {
  89642. $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
  89643. }
  89644. $count_args = count($args);
  89645. for ($i = 0; $i < $count_args; $i++) {
  89646. if ($args[$i] == '>') {
  89647. $mode = 'wb';
  89648. $outputfile = $args[$i+1];
  89649. break;
  89650. } elseif ($args[$i] == '>>') {
  89651. $mode = 'ab+';
  89652. $outputfile = $args[$i+1];
  89653. break;
  89654. } else {
  89655. $files[] = $args[$i];
  89656. }
  89657. }
  89658. $outputfd = false;
  89659. if (isset($mode)) {
  89660. if (!$outputfd = fopen($outputfile, $mode)) {
  89661. $err = System::raiseError("Could not open $outputfile");
  89662. return $err;
  89663. }
  89664. $ret = true;
  89665. }
  89666. foreach ($files as $file) {
  89667. if (!$fd = fopen($file, 'r')) {
  89668. System::raiseError("Could not open $file");
  89669. continue;
  89670. }
  89671. while ($cont = fread($fd, 2048)) {
  89672. if (is_resource($outputfd)) {
  89673. fwrite($outputfd, $cont);
  89674. } else {
  89675. $ret .= $cont;
  89676. }
  89677. }
  89678. fclose($fd);
  89679. }
  89680. if (is_resource($outputfd)) {
  89681. fclose($outputfd);
  89682. }
  89683. return $ret;
  89684. }
  89685. /**
  89686. * Creates temporary files or directories. This function will remove
  89687. * the created files when the scripts finish its execution.
  89688. *
  89689. * Usage:
  89690. * 1) $tempfile = System::mktemp("prefix");
  89691. * 2) $tempdir = System::mktemp("-d prefix");
  89692. * 3) $tempfile = System::mktemp();
  89693. * 4) $tempfile = System::mktemp("-t /var/tmp prefix");
  89694. *
  89695. * prefix -> The string that will be prepended to the temp name
  89696. * (defaults to "tmp").
  89697. * -d -> A temporary dir will be created instead of a file.
  89698. * -t -> The target dir where the temporary (file|dir) will be created. If
  89699. * this param is missing by default the env vars TMP on Windows or
  89700. * TMPDIR in Unix will be used. If these vars are also missing
  89701. * c:\windows\temp or /tmp will be used.
  89702. *
  89703. * @param string $args The arguments
  89704. * @return mixed the full path of the created (file|dir) or false
  89705. * @see System::tmpdir()
  89706. */
  89707. public static function mktemp($args = null)
  89708. {
  89709. static $first_time = true;
  89710. $opts = System::_parseArgs($args, 't:d');
  89711. if (PEAR::isError($opts)) {
  89712. return System::raiseError($opts);
  89713. }
  89714. foreach ($opts[0] as $opt) {
  89715. if ($opt[0] == 'd') {
  89716. $tmp_is_dir = true;
  89717. } elseif ($opt[0] == 't') {
  89718. $tmpdir = $opt[1];
  89719. }
  89720. }
  89721. $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp';
  89722. if (!isset($tmpdir)) {
  89723. $tmpdir = System::tmpdir();
  89724. }
  89725. if (!System::mkDir(array('-p', $tmpdir))) {
  89726. return false;
  89727. }
  89728. $tmp = tempnam($tmpdir, $prefix);
  89729. if (isset($tmp_is_dir)) {
  89730. unlink($tmp); // be careful possible race condition here
  89731. if (!mkdir($tmp, 0700)) {
  89732. return System::raiseError("Unable to create temporary directory $tmpdir");
  89733. }
  89734. }
  89735. $GLOBALS['_System_temp_files'][] = $tmp;
  89736. if (isset($tmp_is_dir)) {
  89737. //$GLOBALS['_System_temp_files'][] = dirname($tmp);
  89738. }
  89739. if ($first_time) {
  89740. PEAR::registerShutdownFunc(array('System', '_removeTmpFiles'));
  89741. $first_time = false;
  89742. }
  89743. return $tmp;
  89744. }
  89745. /**
  89746. * Remove temporary files created my mkTemp. This function is executed
  89747. * at script shutdown time
  89748. */
  89749. public static function _removeTmpFiles()
  89750. {
  89751. if (count($GLOBALS['_System_temp_files'])) {
  89752. $delete = $GLOBALS['_System_temp_files'];
  89753. array_unshift($delete, '-r');
  89754. System::rm($delete);
  89755. $GLOBALS['_System_temp_files'] = array();
  89756. }
  89757. }
  89758. /**
  89759. * Get the path of the temporal directory set in the system
  89760. * by looking in its environments variables.
  89761. * Note: php.ini-recommended removes the "E" from the variables_order setting,
  89762. * making unavaible the $_ENV array, that s why we do tests with _ENV
  89763. *
  89764. * @return string The temporary directory on the system
  89765. */
  89766. public static function tmpdir()
  89767. {
  89768. if (OS_WINDOWS) {
  89769. if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) {
  89770. return $var;
  89771. }
  89772. if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) {
  89773. return $var;
  89774. }
  89775. if ($var = isset($_ENV['USERPROFILE']) ? $_ENV['USERPROFILE'] : getenv('USERPROFILE')) {
  89776. return $var;
  89777. }
  89778. if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) {
  89779. return $var;
  89780. }
  89781. return getenv('SystemRoot') . '\temp';
  89782. }
  89783. if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) {
  89784. return $var;
  89785. }
  89786. return realpath(function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : '/tmp');
  89787. }
  89788. /**
  89789. * The "which" command (show the full path of a command)
  89790. *
  89791. * @param string $program The command to search for
  89792. * @param mixed $fallback Value to return if $program is not found
  89793. *
  89794. * @return mixed A string with the full path or false if not found
  89795. * @author Stig Bakken <ssb@php.net>
  89796. */
  89797. public static function which($program, $fallback = false)
  89798. {
  89799. // enforce API
  89800. if (!is_string($program) || '' == $program) {
  89801. return $fallback;
  89802. }
  89803. // full path given
  89804. if (basename($program) != $program) {
  89805. $path_elements[] = dirname($program);
  89806. $program = basename($program);
  89807. } else {
  89808. $path = getenv('PATH');
  89809. if (!$path) {
  89810. $path = getenv('Path'); // some OSes are just stupid enough to do this
  89811. }
  89812. $path_elements = explode(PATH_SEPARATOR, $path);
  89813. }
  89814. if (OS_WINDOWS) {
  89815. $exe_suffixes = getenv('PATHEXT')
  89816. ? explode(PATH_SEPARATOR, getenv('PATHEXT'))
  89817. : array('.exe','.bat','.cmd','.com');
  89818. // allow passing a command.exe param
  89819. if (strpos($program, '.') !== false) {
  89820. array_unshift($exe_suffixes, '');
  89821. }
  89822. } else {
  89823. $exe_suffixes = array('');
  89824. }
  89825. foreach ($exe_suffixes as $suff) {
  89826. foreach ($path_elements as $dir) {
  89827. $file = $dir . DIRECTORY_SEPARATOR . $program . $suff;
  89828. // It's possible to run a .bat on Windows that is_executable
  89829. // would return false for. The is_executable check is meaningless...
  89830. if (OS_WINDOWS) {
  89831. if (file_exists($file)) {
  89832. return $file;
  89833. }
  89834. } else {
  89835. if (is_executable($file)) {
  89836. return $file;
  89837. }
  89838. }
  89839. }
  89840. }
  89841. return $fallback;
  89842. }
  89843. /**
  89844. * The "find" command
  89845. *
  89846. * Usage:
  89847. *
  89848. * System::find($dir);
  89849. * System::find("$dir -type d");
  89850. * System::find("$dir -type f");
  89851. * System::find("$dir -name *.php");
  89852. * System::find("$dir -name *.php -name *.htm*");
  89853. * System::find("$dir -maxdepth 1");
  89854. *
  89855. * Params implemented:
  89856. * $dir -> Start the search at this directory
  89857. * -type d -> return only directories
  89858. * -type f -> return only files
  89859. * -maxdepth <n> -> max depth of recursion
  89860. * -name <pattern> -> search pattern (bash style). Multiple -name param allowed
  89861. *
  89862. * @param mixed Either array or string with the command line
  89863. * @return array Array of found files
  89864. */
  89865. public static function find($args)
  89866. {
  89867. if (!is_array($args)) {
  89868. $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
  89869. }
  89870. $dir = realpath(array_shift($args));
  89871. if (!$dir) {
  89872. return array();
  89873. }
  89874. $patterns = array();
  89875. $depth = 0;
  89876. $do_files = $do_dirs = true;
  89877. $args_count = count($args);
  89878. for ($i = 0; $i < $args_count; $i++) {
  89879. switch ($args[$i]) {
  89880. case '-type':
  89881. if (in_array($args[$i+1], array('d', 'f'))) {
  89882. if ($args[$i+1] == 'd') {
  89883. $do_files = false;
  89884. } else {
  89885. $do_dirs = false;
  89886. }
  89887. }
  89888. $i++;
  89889. break;
  89890. case '-name':
  89891. $name = preg_quote($args[$i+1], '#');
  89892. // our magic characters ? and * have just been escaped,
  89893. // so now we change the escaped versions to PCRE operators
  89894. $name = strtr($name, array('\?' => '.', '\*' => '.*'));
  89895. $patterns[] = '('.$name.')';
  89896. $i++;
  89897. break;
  89898. case '-maxdepth':
  89899. $depth = $args[$i+1];
  89900. break;
  89901. }
  89902. }
  89903. $path = System::_dirToStruct($dir, $depth, 0, true);
  89904. if ($do_files && $do_dirs) {
  89905. $files = array_merge($path['files'], $path['dirs']);
  89906. } elseif ($do_dirs) {
  89907. $files = $path['dirs'];
  89908. } else {
  89909. $files = $path['files'];
  89910. }
  89911. if (count($patterns)) {
  89912. $dsq = preg_quote(DIRECTORY_SEPARATOR, '#');
  89913. $pattern = '#(^|'.$dsq.')'.implode('|', $patterns).'($|'.$dsq.')#';
  89914. $ret = array();
  89915. $files_count = count($files);
  89916. for ($i = 0; $i < $files_count; $i++) {
  89917. // only search in the part of the file below the current directory
  89918. $filepart = basename($files[$i]);
  89919. if (preg_match($pattern, $filepart)) {
  89920. $ret[] = $files[$i];
  89921. }
  89922. }
  89923. return $ret;
  89924. }
  89925. return $files;
  89926. }
  89927. }
  89928. <?php
  89929. /**
  89930. * XML_Util
  89931. *
  89932. * XML Utilities package
  89933. *
  89934. * PHP versions 4 and 5
  89935. *
  89936. * LICENSE:
  89937. *
  89938. * Copyright (c) 2003-2008 Stephan Schmidt <schst@php.net>
  89939. * All rights reserved.
  89940. *
  89941. * Redistribution and use in source and binary forms, with or without
  89942. * modification, are permitted provided that the following conditions
  89943. * are met:
  89944. *
  89945. * * Redistributions of source code must retain the above copyright
  89946. * notice, this list of conditions and the following disclaimer.
  89947. * * Redistributions in binary form must reproduce the above copyright
  89948. * notice, this list of conditions and the following disclaimer in the
  89949. * documentation and/or other materials provided with the distribution.
  89950. * * The name of the author may not be used to endorse or promote products
  89951. * derived from this software without specific prior written permission.
  89952. *
  89953. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  89954. * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  89955. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  89956. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  89957. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  89958. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  89959. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  89960. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  89961. * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  89962. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  89963. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  89964. *
  89965. * @category XML
  89966. * @package XML_Util
  89967. * @author Stephan Schmidt <schst@php.net>
  89968. * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
  89969. * @license http://opensource.org/licenses/bsd-license New BSD License
  89970. * @version CVS: $Id$
  89971. * @link http://pear.php.net/package/XML_Util
  89972. */
  89973. /**
  89974. * Error code for invalid chars in XML name
  89975. */
  89976. define('XML_UTIL_ERROR_INVALID_CHARS', 51);
  89977. /**
  89978. * Error code for invalid chars in XML name
  89979. */
  89980. define('XML_UTIL_ERROR_INVALID_START', 52);
  89981. /**
  89982. * Error code for non-scalar tag content
  89983. */
  89984. define('XML_UTIL_ERROR_NON_SCALAR_CONTENT', 60);
  89985. /**
  89986. * Error code for missing tag name
  89987. */
  89988. define('XML_UTIL_ERROR_NO_TAG_NAME', 61);
  89989. /**
  89990. * Replace XML entities
  89991. */
  89992. define('XML_UTIL_REPLACE_ENTITIES', 1);
  89993. /**
  89994. * Embedd content in a CData Section
  89995. */
  89996. define('XML_UTIL_CDATA_SECTION', 5);
  89997. /**
  89998. * Do not replace entitites
  89999. */
  90000. define('XML_UTIL_ENTITIES_NONE', 0);
  90001. /**
  90002. * Replace all XML entitites
  90003. * This setting will replace <, >, ", ' and &
  90004. */
  90005. define('XML_UTIL_ENTITIES_XML', 1);
  90006. /**
  90007. * Replace only required XML entitites
  90008. * This setting will replace <, " and &
  90009. */
  90010. define('XML_UTIL_ENTITIES_XML_REQUIRED', 2);
  90011. /**
  90012. * Replace HTML entitites
  90013. * @link http://www.php.net/htmlentities
  90014. */
  90015. define('XML_UTIL_ENTITIES_HTML', 3);
  90016. /**
  90017. * Do not collapse any empty tags.
  90018. */
  90019. define('XML_UTIL_COLLAPSE_NONE', 0);
  90020. /**
  90021. * Collapse all empty tags.
  90022. */
  90023. define('XML_UTIL_COLLAPSE_ALL', 1);
  90024. /**
  90025. * Collapse only empty XHTML tags that have no end tag.
  90026. */
  90027. define('XML_UTIL_COLLAPSE_XHTML_ONLY', 2);
  90028. /**
  90029. * Utility class for working with XML documents
  90030. *
  90031. * @category XML
  90032. * @package XML_Util
  90033. * @author Stephan Schmidt <schst@php.net>
  90034. * @copyright 2003-2008 Stephan Schmidt <schst@php.net>
  90035. * @license http://opensource.org/licenses/bsd-license New BSD License
  90036. * @version Release: 1.4.5
  90037. * @link http://pear.php.net/package/XML_Util
  90038. */
  90039. class XML_Util
  90040. {
  90041. /**
  90042. * Return API version
  90043. *
  90044. * @return string $version API version
  90045. */
  90046. public static function apiVersion()
  90047. {
  90048. return '1.4';
  90049. }
  90050. /**
  90051. * Replace XML entities
  90052. *
  90053. * With the optional second parameter, you may select, which
  90054. * entities should be replaced.
  90055. *
  90056. * <code>
  90057. * require_once 'XML/Util.php';
  90058. *
  90059. * // replace XML entites:
  90060. * $string = XML_Util::replaceEntities('This string contains < & >.');
  90061. * </code>
  90062. *
  90063. * With the optional third parameter, you may pass the character encoding
  90064. * <code>
  90065. * require_once 'XML/Util.php';
  90066. *
  90067. * // replace XML entites in UTF-8:
  90068. * $string = XML_Util::replaceEntities(
  90069. * 'This string contains < & > as well as ä, ö, ß, à and ê',
  90070. * XML_UTIL_ENTITIES_HTML,
  90071. * 'UTF-8'
  90072. * );
  90073. * </code>
  90074. *
  90075. * @param string $string string where XML special chars
  90076. * should be replaced
  90077. * @param int $replaceEntities setting for entities in attribute values
  90078. * (one of XML_UTIL_ENTITIES_XML,
  90079. * XML_UTIL_ENTITIES_XML_REQUIRED,
  90080. * XML_UTIL_ENTITIES_HTML)
  90081. * @param string $encoding encoding value (if any)...
  90082. * must be a valid encoding as determined
  90083. * by the htmlentities() function
  90084. *
  90085. * @return string string with replaced chars
  90086. * @see reverseEntities()
  90087. */
  90088. public static function replaceEntities(
  90089. $string, $replaceEntities = XML_UTIL_ENTITIES_XML, $encoding = 'ISO-8859-1'
  90090. ) {
  90091. switch ($replaceEntities) {
  90092. case XML_UTIL_ENTITIES_XML:
  90093. return strtr(
  90094. $string,
  90095. array(
  90096. '&' => '&amp;',
  90097. '>' => '&gt;',
  90098. '<' => '&lt;',
  90099. '"' => '&quot;',
  90100. '\'' => '&apos;'
  90101. )
  90102. );
  90103. break;
  90104. case XML_UTIL_ENTITIES_XML_REQUIRED:
  90105. return strtr(
  90106. $string,
  90107. array(
  90108. '&' => '&amp;',
  90109. '<' => '&lt;',
  90110. '"' => '&quot;'
  90111. )
  90112. );
  90113. break;
  90114. case XML_UTIL_ENTITIES_HTML:
  90115. return htmlentities($string, ENT_COMPAT, $encoding);
  90116. break;
  90117. }
  90118. return $string;
  90119. }
  90120. /**
  90121. * Reverse XML entities
  90122. *
  90123. * With the optional second parameter, you may select, which
  90124. * entities should be reversed.
  90125. *
  90126. * <code>
  90127. * require_once 'XML/Util.php';
  90128. *
  90129. * // reverse XML entites:
  90130. * $string = XML_Util::reverseEntities('This string contains &lt; &amp; &gt;.');
  90131. * </code>
  90132. *
  90133. * With the optional third parameter, you may pass the character encoding
  90134. * <code>
  90135. * require_once 'XML/Util.php';
  90136. *
  90137. * // reverse XML entites in UTF-8:
  90138. * $string = XML_Util::reverseEntities(
  90139. * 'This string contains &lt; &amp; &gt; as well as'
  90140. * . ' &auml;, &ouml;, &szlig;, &agrave; and &ecirc;',
  90141. * XML_UTIL_ENTITIES_HTML,
  90142. * 'UTF-8'
  90143. * );
  90144. * </code>
  90145. *
  90146. * @param string $string string where XML special chars
  90147. * should be replaced
  90148. * @param int $replaceEntities setting for entities in attribute values
  90149. * (one of XML_UTIL_ENTITIES_XML,
  90150. * XML_UTIL_ENTITIES_XML_REQUIRED,
  90151. * XML_UTIL_ENTITIES_HTML)
  90152. * @param string $encoding encoding value (if any)...
  90153. * must be a valid encoding as determined
  90154. * by the html_entity_decode() function
  90155. *
  90156. * @return string string with replaced chars
  90157. * @see replaceEntities()
  90158. */
  90159. public static function reverseEntities(
  90160. $string, $replaceEntities = XML_UTIL_ENTITIES_XML, $encoding = 'ISO-8859-1'
  90161. ) {
  90162. switch ($replaceEntities) {
  90163. case XML_UTIL_ENTITIES_XML:
  90164. return strtr(
  90165. $string,
  90166. array(
  90167. '&amp;' => '&',
  90168. '&gt;' => '>',
  90169. '&lt;' => '<',
  90170. '&quot;' => '"',
  90171. '&apos;' => '\''
  90172. )
  90173. );
  90174. break;
  90175. case XML_UTIL_ENTITIES_XML_REQUIRED:
  90176. return strtr(
  90177. $string,
  90178. array(
  90179. '&amp;' => '&',
  90180. '&lt;' => '<',
  90181. '&quot;' => '"'
  90182. )
  90183. );
  90184. break;
  90185. case XML_UTIL_ENTITIES_HTML:
  90186. return html_entity_decode($string, ENT_COMPAT, $encoding);
  90187. break;
  90188. }
  90189. return $string;
  90190. }
  90191. /**
  90192. * Build an xml declaration
  90193. *
  90194. * <code>
  90195. * require_once 'XML/Util.php';
  90196. *
  90197. * // get an XML declaration:
  90198. * $xmlDecl = XML_Util::getXMLDeclaration('1.0', 'UTF-8', true);
  90199. * </code>
  90200. *
  90201. * @param string $version xml version
  90202. * @param string $encoding character encoding
  90203. * @param bool $standalone document is standalone (or not)
  90204. *
  90205. * @return string xml declaration
  90206. * @uses attributesToString() to serialize the attributes of the
  90207. * XML declaration
  90208. */
  90209. public static function getXMLDeclaration(
  90210. $version = '1.0', $encoding = null, $standalone = null
  90211. ) {
  90212. $attributes = array(
  90213. 'version' => $version,
  90214. );
  90215. // add encoding
  90216. if ($encoding !== null) {
  90217. $attributes['encoding'] = $encoding;
  90218. }
  90219. // add standalone, if specified
  90220. if ($standalone !== null) {
  90221. $attributes['standalone'] = $standalone ? 'yes' : 'no';
  90222. }
  90223. return sprintf(
  90224. '<?xml%s?>',
  90225. XML_Util::attributesToString($attributes, false)
  90226. );
  90227. }
  90228. /**
  90229. * Build a document type declaration
  90230. *
  90231. * <code>
  90232. * require_once 'XML/Util.php';
  90233. *
  90234. * // get a doctype declaration:
  90235. * $xmlDecl = XML_Util::getDocTypeDeclaration('rootTag','myDocType.dtd');
  90236. * </code>
  90237. *
  90238. * @param string $root name of the root tag
  90239. * @param string $uri uri of the doctype definition
  90240. * (or array with uri and public id)
  90241. * @param string $internalDtd internal dtd entries
  90242. *
  90243. * @return string doctype declaration
  90244. * @since 0.2
  90245. */
  90246. public static function getDocTypeDeclaration(
  90247. $root, $uri = null, $internalDtd = null
  90248. ) {
  90249. if (is_array($uri)) {
  90250. $ref = sprintf(' PUBLIC "%s" "%s"', $uri['id'], $uri['uri']);
  90251. } elseif (!empty($uri)) {
  90252. $ref = sprintf(' SYSTEM "%s"', $uri);
  90253. } else {
  90254. $ref = '';
  90255. }
  90256. if (empty($internalDtd)) {
  90257. return sprintf('<!DOCTYPE %s%s>', $root, $ref);
  90258. } else {
  90259. return sprintf("<!DOCTYPE %s%s [\n%s\n]>", $root, $ref, $internalDtd);
  90260. }
  90261. }
  90262. /**
  90263. * Create string representation of an attribute list
  90264. *
  90265. * <code>
  90266. * require_once 'XML/Util.php';
  90267. *
  90268. * // build an attribute string
  90269. * $att = array(
  90270. * 'foo' => 'bar',
  90271. * 'argh' => 'tomato'
  90272. * );
  90273. *
  90274. * $attList = XML_Util::attributesToString($att);
  90275. * </code>
  90276. *
  90277. * @param array $attributes attribute array
  90278. * @param bool|array $sort sort attribute list alphabetically,
  90279. * may also be an assoc array containing
  90280. * the keys 'sort', 'multiline', 'indent',
  90281. * 'linebreak' and 'entities'
  90282. * @param bool $multiline use linebreaks, if more than
  90283. * one attribute is given
  90284. * @param string $indent string used for indentation of
  90285. * multiline attributes
  90286. * @param string $linebreak string used for linebreaks of
  90287. * multiline attributes
  90288. * @param int $entities setting for entities in attribute values
  90289. * (one of XML_UTIL_ENTITIES_NONE,
  90290. * XML_UTIL_ENTITIES_XML,
  90291. * XML_UTIL_ENTITIES_XML_REQUIRED,
  90292. * XML_UTIL_ENTITIES_HTML)
  90293. *
  90294. * @return string string representation of the attributes
  90295. * @uses replaceEntities() to replace XML entities in attribute values
  90296. * @todo allow sort also to be an options array
  90297. */
  90298. public static function attributesToString(
  90299. $attributes, $sort = true, $multiline = false,
  90300. $indent = ' ', $linebreak = "\n", $entities = XML_UTIL_ENTITIES_XML
  90301. ) {
  90302. /*
  90303. * second parameter may be an array
  90304. */
  90305. if (is_array($sort)) {
  90306. if (isset($sort['multiline'])) {
  90307. $multiline = $sort['multiline'];
  90308. }
  90309. if (isset($sort['indent'])) {
  90310. $indent = $sort['indent'];
  90311. }
  90312. if (isset($sort['linebreak'])) {
  90313. $multiline = $sort['linebreak'];
  90314. }
  90315. if (isset($sort['entities'])) {
  90316. $entities = $sort['entities'];
  90317. }
  90318. if (isset($sort['sort'])) {
  90319. $sort = $sort['sort'];
  90320. } else {
  90321. $sort = true;
  90322. }
  90323. }
  90324. $string = '';
  90325. if (is_array($attributes) && !empty($attributes)) {
  90326. if ($sort) {
  90327. ksort($attributes);
  90328. }
  90329. if (!$multiline || count($attributes) == 1) {
  90330. foreach ($attributes as $key => $value) {
  90331. if ($entities != XML_UTIL_ENTITIES_NONE) {
  90332. if ($entities === XML_UTIL_CDATA_SECTION) {
  90333. $entities = XML_UTIL_ENTITIES_XML;
  90334. }
  90335. $value = XML_Util::replaceEntities($value, $entities);
  90336. }
  90337. $string .= ' ' . $key . '="' . $value . '"';
  90338. }
  90339. } else {
  90340. $first = true;
  90341. foreach ($attributes as $key => $value) {
  90342. if ($entities != XML_UTIL_ENTITIES_NONE) {
  90343. $value = XML_Util::replaceEntities($value, $entities);
  90344. }
  90345. if ($first) {
  90346. $string .= ' ' . $key . '="' . $value . '"';
  90347. $first = false;
  90348. } else {
  90349. $string .= $linebreak . $indent . $key . '="' . $value . '"';
  90350. }
  90351. }
  90352. }
  90353. }
  90354. return $string;
  90355. }
  90356. /**
  90357. * Collapses empty tags.
  90358. *
  90359. * @param string $xml XML
  90360. * @param int $mode Whether to collapse all empty tags (XML_UTIL_COLLAPSE_ALL)
  90361. * or only XHTML (XML_UTIL_COLLAPSE_XHTML_ONLY) ones.
  90362. *
  90363. * @return string XML
  90364. */
  90365. public static function collapseEmptyTags($xml, $mode = XML_UTIL_COLLAPSE_ALL)
  90366. {
  90367. if (preg_match('~<([^>])+/>~s', $xml, $matches)) {
  90368. // it's already an empty tag
  90369. return $xml;
  90370. }
  90371. switch ($mode) {
  90372. case XML_UTIL_COLLAPSE_ALL:
  90373. $preg1 =
  90374. '~<' .
  90375. '(?:' .
  90376. '(https?://[^:\s]+:\w+)' . // <http://foo.com:bar ($1)
  90377. '|(\w+:\w+)' . // <foo:bar ($2)
  90378. '|(\w+)' . // <foo ($3)
  90379. ')+' .
  90380. '([^>]*)' . // attributes ($4)
  90381. '>' .
  90382. '<\/(\1|\2|\3)>' . // 1, 2, or 3 again ($5)
  90383. '~s'
  90384. ;
  90385. $preg2 =
  90386. '<' .
  90387. '${1}${2}${3}' . // tag (only one should have been populated)
  90388. '${4}' . // attributes
  90389. ' />'
  90390. ;
  90391. return (preg_replace($preg1, $preg2, $xml)?:$xml);
  90392. break;
  90393. case XML_UTIL_COLLAPSE_XHTML_ONLY:
  90394. return (
  90395. preg_replace(
  90396. '/<(area|base(?:font)?|br|col|frame|hr|img|input|isindex|link|meta|'
  90397. . 'param)([^>]*)><\/\\1>/s',
  90398. '<\\1\\2 />',
  90399. $xml
  90400. ) ?: $xml
  90401. );
  90402. break;
  90403. case XML_UTIL_COLLAPSE_NONE:
  90404. // fall thru
  90405. default:
  90406. return $xml;
  90407. }
  90408. }
  90409. /**
  90410. * Create a tag
  90411. *
  90412. * This method will call XML_Util::createTagFromArray(), which
  90413. * is more flexible.
  90414. *
  90415. * <code>
  90416. * require_once 'XML/Util.php';
  90417. *
  90418. * // create an XML tag:
  90419. * $tag = XML_Util::createTag('myNs:myTag',
  90420. * array('foo' => 'bar'),
  90421. * 'This is inside the tag',
  90422. * 'http://www.w3c.org/myNs#');
  90423. * </code>
  90424. *
  90425. * @param string $qname qualified tagname (including namespace)
  90426. * @param array $attributes array containg attributes
  90427. * @param mixed $content the content
  90428. * @param string $namespaceUri URI of the namespace
  90429. * @param int $replaceEntities whether to replace XML special chars in
  90430. * content, embedd it in a CData section
  90431. * or none of both
  90432. * @param bool $multiline whether to create a multiline tag where
  90433. * each attribute gets written to a single line
  90434. * @param string $indent string used to indent attributes
  90435. * (_auto indents attributes so they start
  90436. * at the same column)
  90437. * @param string $linebreak string used for linebreaks
  90438. * @param bool $sortAttributes Whether to sort the attributes or not
  90439. * @param int $collapseTagMode How to handle a content-less, and thus collapseable, tag
  90440. *
  90441. * @return string XML tag
  90442. * @see createTagFromArray()
  90443. * @uses createTagFromArray() to create the tag
  90444. */
  90445. public static function createTag(
  90446. $qname, $attributes = array(), $content = null,
  90447. $namespaceUri = null, $replaceEntities = XML_UTIL_REPLACE_ENTITIES,
  90448. $multiline = false, $indent = '_auto', $linebreak = "\n",
  90449. $sortAttributes = true, $collapseTagMode = XML_UTIL_COLLAPSE_ALL
  90450. ) {
  90451. $tag = array(
  90452. 'qname' => $qname,
  90453. 'attributes' => $attributes
  90454. );
  90455. // add tag content
  90456. if ($content !== null) {
  90457. $tag['content'] = $content;
  90458. }
  90459. // add namespace Uri
  90460. if ($namespaceUri !== null) {
  90461. $tag['namespaceUri'] = $namespaceUri;
  90462. }
  90463. return XML_Util::createTagFromArray(
  90464. $tag, $replaceEntities, $multiline,
  90465. $indent, $linebreak, $sortAttributes,
  90466. $collapseTagMode
  90467. );
  90468. }
  90469. /**
  90470. * Create a tag from an array.
  90471. * This method awaits an array in the following format
  90472. * <pre>
  90473. * array(
  90474. * // qualified name of the tag
  90475. * 'qname' => $qname
  90476. *
  90477. * // namespace prefix (optional, if qname is specified or no namespace)
  90478. * 'namespace' => $namespace
  90479. *
  90480. * // local part of the tagname (optional, if qname is specified)
  90481. * 'localpart' => $localpart,
  90482. *
  90483. * // array containing all attributes (optional)
  90484. * 'attributes' => array(),
  90485. *
  90486. * // tag content (optional)
  90487. * 'content' => $content,
  90488. *
  90489. * // namespaceUri for the given namespace (optional)
  90490. * 'namespaceUri' => $namespaceUri
  90491. * )
  90492. * </pre>
  90493. *
  90494. * <code>
  90495. * require_once 'XML/Util.php';
  90496. *
  90497. * $tag = array(
  90498. * 'qname' => 'foo:bar',
  90499. * 'namespaceUri' => 'http://foo.com',
  90500. * 'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'),
  90501. * 'content' => 'I\'m inside the tag',
  90502. * );
  90503. * // creating a tag with qualified name and namespaceUri
  90504. * $string = XML_Util::createTagFromArray($tag);
  90505. * </code>
  90506. *
  90507. * @param array $tag tag definition
  90508. * @param int $replaceEntities whether to replace XML special chars in
  90509. * content, embedd it in a CData section
  90510. * or none of both
  90511. * @param bool $multiline whether to create a multiline tag where each
  90512. * attribute gets written to a single line
  90513. * @param string $indent string used to indent attributes
  90514. * (_auto indents attributes so they start
  90515. * at the same column)
  90516. * @param string $linebreak string used for linebreaks
  90517. * @param bool $sortAttributes Whether to sort the attributes or not
  90518. * @param int $collapseTagMode How to handle a content-less, and thus collapseable, tag
  90519. *
  90520. * @return string XML tag
  90521. *
  90522. * @see createTag()
  90523. * @uses attributesToString() to serialize the attributes of the tag
  90524. * @uses splitQualifiedName() to get local part and namespace of a qualified name
  90525. * @uses createCDataSection()
  90526. * @uses collapseEmptyTags()
  90527. * @uses raiseError()
  90528. */
  90529. public static function createTagFromArray(
  90530. $tag, $replaceEntities = XML_UTIL_REPLACE_ENTITIES,
  90531. $multiline = false, $indent = '_auto', $linebreak = "\n",
  90532. $sortAttributes = true, $collapseTagMode = XML_UTIL_COLLAPSE_ALL
  90533. ) {
  90534. if (isset($tag['content']) && !is_scalar($tag['content'])) {
  90535. return XML_Util::raiseError(
  90536. 'Supplied non-scalar value as tag content',
  90537. XML_UTIL_ERROR_NON_SCALAR_CONTENT
  90538. );
  90539. }
  90540. if (!isset($tag['qname']) && !isset($tag['localPart'])) {
  90541. return XML_Util::raiseError(
  90542. 'You must either supply a qualified name '
  90543. . '(qname) or local tag name (localPart).',
  90544. XML_UTIL_ERROR_NO_TAG_NAME
  90545. );
  90546. }
  90547. // if no attributes hav been set, use empty attributes
  90548. if (!isset($tag['attributes']) || !is_array($tag['attributes'])) {
  90549. $tag['attributes'] = array();
  90550. }
  90551. if (isset($tag['namespaces'])) {
  90552. foreach ($tag['namespaces'] as $ns => $uri) {
  90553. $tag['attributes']['xmlns:' . $ns] = $uri;
  90554. }
  90555. }
  90556. if (!isset($tag['qname'])) {
  90557. // qualified name is not given
  90558. // check for namespace
  90559. if (isset($tag['namespace']) && !empty($tag['namespace'])) {
  90560. $tag['qname'] = $tag['namespace'] . ':' . $tag['localPart'];
  90561. } else {
  90562. $tag['qname'] = $tag['localPart'];
  90563. }
  90564. } elseif (isset($tag['namespaceUri']) && !isset($tag['namespace'])) {
  90565. // namespace URI is set, but no namespace
  90566. $parts = XML_Util::splitQualifiedName($tag['qname']);
  90567. $tag['localPart'] = $parts['localPart'];
  90568. if (isset($parts['namespace'])) {
  90569. $tag['namespace'] = $parts['namespace'];
  90570. }
  90571. }
  90572. if (isset($tag['namespaceUri']) && !empty($tag['namespaceUri'])) {
  90573. // is a namespace given
  90574. if (isset($tag['namespace']) && !empty($tag['namespace'])) {
  90575. $tag['attributes']['xmlns:' . $tag['namespace']]
  90576. = $tag['namespaceUri'];
  90577. } else {
  90578. // define this Uri as the default namespace
  90579. $tag['attributes']['xmlns'] = $tag['namespaceUri'];
  90580. }
  90581. }
  90582. if (!array_key_exists('content', $tag)) {
  90583. $tag['content'] = '';
  90584. }
  90585. // check for multiline attributes
  90586. if ($multiline === true) {
  90587. if ($indent === '_auto') {
  90588. $indent = str_repeat(' ', (strlen($tag['qname'])+2));
  90589. }
  90590. }
  90591. // create attribute list
  90592. $attList = XML_Util::attributesToString(
  90593. $tag['attributes'],
  90594. $sortAttributes, $multiline, $indent, $linebreak
  90595. );
  90596. switch ($replaceEntities) {
  90597. case XML_UTIL_ENTITIES_NONE:
  90598. break;
  90599. case XML_UTIL_CDATA_SECTION:
  90600. $tag['content'] = XML_Util::createCDataSection($tag['content']);
  90601. break;
  90602. default:
  90603. $tag['content'] = XML_Util::replaceEntities(
  90604. $tag['content'], $replaceEntities
  90605. );
  90606. break;
  90607. }
  90608. $tag = sprintf(
  90609. '<%s%s>%s</%s>', $tag['qname'], $attList, $tag['content'],
  90610. $tag['qname']
  90611. );
  90612. return self::collapseEmptyTags($tag, $collapseTagMode);
  90613. }
  90614. /**
  90615. * Create a start element
  90616. *
  90617. * <code>
  90618. * require_once 'XML/Util.php';
  90619. *
  90620. * // create an XML start element:
  90621. * $tag = XML_Util::createStartElement('myNs:myTag',
  90622. * array('foo' => 'bar') ,'http://www.w3c.org/myNs#');
  90623. * </code>
  90624. *
  90625. * @param string $qname qualified tagname (including namespace)
  90626. * @param array $attributes array containg attributes
  90627. * @param string $namespaceUri URI of the namespace
  90628. * @param bool $multiline whether to create a multiline tag where each
  90629. * attribute gets written to a single line
  90630. * @param string $indent string used to indent attributes (_auto indents
  90631. * attributes so they start at the same column)
  90632. * @param string $linebreak string used for linebreaks
  90633. * @param bool $sortAttributes Whether to sort the attributes or not
  90634. *
  90635. * @return string XML start element
  90636. * @see createEndElement(), createTag()
  90637. */
  90638. public static function createStartElement(
  90639. $qname, $attributes = array(), $namespaceUri = null,
  90640. $multiline = false, $indent = '_auto', $linebreak = "\n",
  90641. $sortAttributes = true
  90642. ) {
  90643. // if no attributes hav been set, use empty attributes
  90644. if (!isset($attributes) || !is_array($attributes)) {
  90645. $attributes = array();
  90646. }
  90647. if ($namespaceUri != null) {
  90648. $parts = XML_Util::splitQualifiedName($qname);
  90649. }
  90650. // check for multiline attributes
  90651. if ($multiline === true) {
  90652. if ($indent === '_auto') {
  90653. $indent = str_repeat(' ', (strlen($qname)+2));
  90654. }
  90655. }
  90656. if ($namespaceUri != null) {
  90657. // is a namespace given
  90658. if (isset($parts['namespace']) && !empty($parts['namespace'])) {
  90659. $attributes['xmlns:' . $parts['namespace']] = $namespaceUri;
  90660. } else {
  90661. // define this Uri as the default namespace
  90662. $attributes['xmlns'] = $namespaceUri;
  90663. }
  90664. }
  90665. // create attribute list
  90666. $attList = XML_Util::attributesToString(
  90667. $attributes, $sortAttributes,
  90668. $multiline, $indent, $linebreak
  90669. );
  90670. $element = sprintf('<%s%s>', $qname, $attList);
  90671. return $element;
  90672. }
  90673. /**
  90674. * Create an end element
  90675. *
  90676. * <code>
  90677. * require_once 'XML/Util.php';
  90678. *
  90679. * // create an XML start element:
  90680. * $tag = XML_Util::createEndElement('myNs:myTag');
  90681. * </code>
  90682. *
  90683. * @param string $qname qualified tagname (including namespace)
  90684. *
  90685. * @return string XML end element
  90686. * @see createStartElement(), createTag()
  90687. */
  90688. public static function createEndElement($qname)
  90689. {
  90690. $element = sprintf('</%s>', $qname);
  90691. return $element;
  90692. }
  90693. /**
  90694. * Create an XML comment
  90695. *
  90696. * <code>
  90697. * require_once 'XML/Util.php';
  90698. *
  90699. * // create an XML start element:
  90700. * $tag = XML_Util::createComment('I am a comment');
  90701. * </code>
  90702. *
  90703. * @param string $content content of the comment
  90704. *
  90705. * @return string XML comment
  90706. */
  90707. public static function createComment($content)
  90708. {
  90709. $comment = sprintf('<!-- %s -->', $content);
  90710. return $comment;
  90711. }
  90712. /**
  90713. * Create a CData section
  90714. *
  90715. * <code>
  90716. * require_once 'XML/Util.php';
  90717. *
  90718. * // create a CData section
  90719. * $tag = XML_Util::createCDataSection('I am content.');
  90720. * </code>
  90721. *
  90722. * @param string $data data of the CData section
  90723. *
  90724. * @return string CData section with content
  90725. */
  90726. public static function createCDataSection($data)
  90727. {
  90728. return sprintf(
  90729. '<![CDATA[%s]]>',
  90730. preg_replace('/\]\]>/', ']]]]><![CDATA[>', strval($data))
  90731. );
  90732. }
  90733. /**
  90734. * Split qualified name and return namespace and local part
  90735. *
  90736. * <code>
  90737. * require_once 'XML/Util.php';
  90738. *
  90739. * // split qualified tag
  90740. * $parts = XML_Util::splitQualifiedName('xslt:stylesheet');
  90741. * </code>
  90742. * the returned array will contain two elements:
  90743. * <pre>
  90744. * array(
  90745. * 'namespace' => 'xslt',
  90746. * 'localPart' => 'stylesheet'
  90747. * );
  90748. * </pre>
  90749. *
  90750. * @param string $qname qualified tag name
  90751. * @param string $defaultNs default namespace (optional)
  90752. *
  90753. * @return array array containing namespace and local part
  90754. */
  90755. public static function splitQualifiedName($qname, $defaultNs = null)
  90756. {
  90757. if (strstr($qname, ':')) {
  90758. $tmp = explode(':', $qname);
  90759. return array(
  90760. 'namespace' => $tmp[0],
  90761. 'localPart' => $tmp[1]
  90762. );
  90763. }
  90764. return array(
  90765. 'namespace' => $defaultNs,
  90766. 'localPart' => $qname
  90767. );
  90768. }
  90769. /**
  90770. * Check, whether string is valid XML name
  90771. *
  90772. * <p>XML names are used for tagname, attribute names and various
  90773. * other, lesser known entities.</p>
  90774. * <p>An XML name may only consist of alphanumeric characters,
  90775. * dashes, undescores and periods, and has to start with a letter
  90776. * or an underscore.</p>
  90777. *
  90778. * <code>
  90779. * require_once 'XML/Util.php';
  90780. *
  90781. * // verify tag name
  90782. * $result = XML_Util::isValidName('invalidTag?');
  90783. * if (is_a($result, 'PEAR_Error')) {
  90784. * print 'Invalid XML name: ' . $result->getMessage();
  90785. * }
  90786. * </code>
  90787. *
  90788. * @param string $string string that should be checked
  90789. *
  90790. * @return mixed true, if string is a valid XML name, PEAR error otherwise
  90791. *
  90792. * @todo support for other charsets
  90793. * @todo PEAR CS - unable to avoid 85-char limit on second preg_match
  90794. */
  90795. public static function isValidName($string)
  90796. {
  90797. // check for invalid chars
  90798. if (!is_string($string) || !strlen($string) || !preg_match('/^[[:alpha:]_]\\z/', $string[0])) {
  90799. return XML_Util::raiseError(
  90800. 'XML names may only start with letter or underscore',
  90801. XML_UTIL_ERROR_INVALID_START
  90802. );
  90803. }
  90804. // check for invalid chars
  90805. $match = preg_match(
  90806. '/^([[:alpha:]_]([[:alnum:]\-\.]*)?:)?'
  90807. . '[[:alpha:]_]([[:alnum:]\_\-\.]+)?\\z/',
  90808. $string
  90809. );
  90810. if (!$match) {
  90811. return XML_Util::raiseError(
  90812. 'XML names may only contain alphanumeric '
  90813. . 'chars, period, hyphen, colon and underscores',
  90814. XML_UTIL_ERROR_INVALID_CHARS
  90815. );
  90816. }
  90817. // XML name is valid
  90818. return true;
  90819. }
  90820. /**
  90821. * Replacement for XML_Util::raiseError
  90822. *
  90823. * Avoids the necessity to always require
  90824. * PEAR.php
  90825. *
  90826. * @param string $msg error message
  90827. * @param int $code error code
  90828. *
  90829. * @return PEAR_Error
  90830. * @todo PEAR CS - should this use include_once instead?
  90831. */
  90832. public static function raiseError($msg, $code)
  90833. {
  90834. include_once 'phar://go-pear.phar/' . 'PEAR.php';
  90835. return PEAR::raiseError($msg, $code);
  90836. }
  90837. }
  90838. ?>
  90839. ŸƒŠa>Ø�ÓLRoÝXìvµj_���GBMB