@@ -426,33 +426,354 @@ protected function _testObsoleteConstants($content)
426426 foreach (self ::$ _constants as $ row ) {
427427 list ($ constant , $ class , $ replacement ) = $ row ;
428428 if ($ class ) {
429- $ fullyQualified = "{$ class }:: {$ constant }" ;
430- $ regex = preg_quote ($ fullyQualified );
431- if ($ this ->_isClassOrInterface ($ content , $ class )) {
432- $ regex .= '| ' . $ this ->_getClassConstantDefinitionRegExp ($ constant )
433- . '| ' . preg_quote ("self:: {$ constant }" , '/ ' )
434- . '| ' . preg_quote ("static:: {$ constant }" , '/ ' );
435- } elseif ($ this ->_isDirectDescendant ($ content , $ class )) {
436- $ regex .= '| ' . preg_quote ("parent:: {$ constant }" , '/ ' );
437- if (!$ this ->_isClassConstantDefined ($ content , $ constant )) {
438- $ regex .= '| ' . preg_quote (
439- "self:: {$ constant }" ,
440- '/ '
441- ) . '| ' . preg_quote (
442- "static:: {$ constant }" ,
443- '/ '
429+ $ class = ltrim ($ class , '\\' );
430+ $ this ->checkConstantWithFullClasspath ($ constant , $ class , $ replacement , $ content );
431+ $ this ->checkConstantWithClasspath ($ constant , $ class , $ replacement , $ content );
432+ } else {
433+ $ regex = '\b ' . preg_quote ($ constant , '/ ' ) . '\b ' ;
434+ $ this ->checkExistenceOfObsoleteConstants ($ regex , '' , $ content , $ constant , $ replacement , $ class );
435+ }
436+ }
437+ }
438+
439+ /**
440+ * Build regular expression from Obsolete Constants with correspond to contents
441+ *
442+ * @param string $classPartialPath
443+ * @param string $content
444+ * @param string $constant
445+ * @return string
446+ */
447+ private function buildRegExFromObsoleteConstant ($ classPartialPath , $ content , $ constant )
448+ {
449+ $ regex = preg_quote ("{$ classPartialPath }:: {$ constant }" );
450+ if ($ this ->_isClassOrInterface ($ content , $ classPartialPath )) {
451+ $ regex .= '| ' . $ this ->_getClassConstantDefinitionRegExp ($ constant )
452+ . '| ' . preg_quote ("self:: {$ constant }" , '/ ' )
453+ . '| ' . preg_quote ("static:: {$ constant }" , '/ ' );
454+ } elseif ($ this ->_isDirectDescendant ($ content , $ classPartialPath )) {
455+ $ regex .= '| ' . preg_quote ("parent:: {$ constant }" , '/ ' );
456+ if (!$ this ->_isClassConstantDefined ($ content , $ constant )) {
457+ $ regex .= '| ' . preg_quote ("self:: {$ constant }" , '/ ' ) . '| ' . preg_quote ("static:: {$ constant }" , '/ ' );
458+ }
459+ }
460+ return $ regex ;
461+ }
462+
463+ /**
464+ * Checks condition of using full classpath in 'use' with 'as' (Example: 'use A\B\C as D')
465+ * where A\B\C is the class where the constant is obsolete
466+ *
467+ * @param string $constant
468+ * @param string $class
469+ * @param string $replacement
470+ * @param string $content
471+ */
472+ private function checkConstantWithFullClasspath ($ constant , $ class , $ replacement , $ content )
473+ {
474+ $ constantRegex = preg_quote ($ constant , '/ ' );
475+ $ classRegex = preg_quote ($ class );
476+ $ this ->checkExistenceOfObsoleteConstants (
477+ $ constantRegex ,
478+ $ classRegex ,
479+ $ content ,
480+ "{$ class }:: {$ constant }" ,
481+ $ replacement ,
482+ $ class
483+ );
484+ }
485+
486+ /**
487+ * Check all combinations of classpath with constant
488+ *
489+ * @param string $constant
490+ * @param string $class
491+ * @param string $replacement
492+ * @param string $content
493+ */
494+ private function checkConstantWithClasspath ($ constant , $ class , $ replacement , $ content )
495+ {
496+ $ classPathParts = explode ('\\' , $ class );
497+ $ classPartialPath = '' ;
498+ for ($ i = count ($ classPathParts ) - 1 ; $ i >= 0 ; $ i --) {
499+ if ($ i === (count ($ classPathParts ) - 1 )) {
500+ $ classPartialPath = $ classPathParts [$ i ] . $ classPartialPath ;
501+ } else {
502+ $ classPartialPath = $ classPathParts [$ i ] . '\\' . $ classPartialPath ;
503+ }
504+ $ constantRegex = $ this ->buildRegExFromObsoleteConstant ($ classPartialPath , $ content , $ constant );
505+ $ regexClassPartialPath = preg_replace ('/ ' . preg_quote ($ classPartialPath ) . '$/ ' , '' , $ class );
506+ $ classRegex = preg_quote ($ regexClassPartialPath . $ classPathParts [$ i ]);
507+ if ($ regexClassPartialPath !== '' ) {
508+ $ classRegex .= '| ' . preg_quote (rtrim ($ regexClassPartialPath , '\\' ));
509+ }
510+ // Checks condition when classpath is distributed over namespace and class definition
511+ $ classRegexNamespaceClass = '/namespace\s+ ' . preg_quote ('\\' ) . '?( ' . $ classRegex . ')(\s|;)(\r?\n)+ '
512+ . 'class\s+ ' . preg_quote ('\\' ) . '?( ' . preg_quote (rtrim ($ classPartialPath , '\\' )) . ')\s*/ ' ;
513+ $ matchNamespaceClass = preg_match ($ classRegexNamespaceClass , $ content );
514+ $ constantRegexPartial = '/\b(?P<classWithConst>([a-zA-Z0-9_ ' . preg_quote ('\\' ) . ']*))( '
515+ . preg_quote (':: ' ) . ')* ' . '( ' . preg_quote ($ constant , '/ ' ) . '\b)(\s*|;)/ ' ;
516+ $ matchConstantPartial = preg_match ($ constantRegexPartial , $ content , $ match );
517+ if (($ matchNamespaceClass === 1 ) && ($ matchConstantPartial === 1 ) && ($ match ['classWithConst ' ] === '' )) {
518+ $ this ->assertSame (
519+ 0 ,
520+ 1 ,
521+ $ this ->_suggestReplacement (sprintf ("Constant '%s' is obsolete. " , $ constant ), $ replacement )
522+ );
523+ } else {
524+ $ this ->checkExistenceOfObsoleteConstants (
525+ $ constantRegex ,
526+ $ classRegex ,
527+ $ content ,
528+ "{$ classPartialPath }:: {$ constant }" ,
529+ $ replacement ,
530+ $ class
531+ );
532+ }
533+ }
534+ }
535+
536+ /**
537+ * Check existence of Obsolete Constant in current content
538+ *
539+ * @param string $constantRegex
540+ * @param string $classRegex
541+ * @param string $content
542+ * @param string $constant
543+ * @param string $replacement
544+ * @param string $class
545+ */
546+ private function checkExistenceOfObsoleteConstants (
547+ $ constantRegex ,
548+ $ classRegex ,
549+ $ content ,
550+ $ constant ,
551+ $ replacement ,
552+ $ class
553+ ) {
554+ $ constantRegexFull = '/\b(?P<constPart>((?P<classWithConst>([a-zA-Z0-9_ ' . preg_quote ('\\' ) . ']*))( '
555+ . preg_quote (':: ' ) . ')* ' . '( ' . $ constantRegex . '\b)))(\s*|;)/ ' ;
556+ $ matchConstant = preg_match_all ($ constantRegexFull , $ content , $ matchConstantString );
557+ $ result = 0 ;
558+ if ($ matchConstant === 1 ) {
559+ if ($ classRegex !== '' ) {
560+ $ classRegexFull = '/(?P<useOrNamespace>(use|namespace))\s+(?P<classPath>( ' . preg_quote ('\\' )
561+ . '?( ' . $ classRegex . ')))(\s+as\s+(?P<classAlias>([\w\d_]+)))?(\s|;)/ ' ;
562+ $ matchClass = preg_match ($ classRegexFull , $ content , $ matchClassString );
563+ if ($ matchClass === 1 ) {
564+ if ($ matchClassString ['classAlias ' ]) {
565+ $ result = $ this ->checkAliasUseNamespace (
566+ $ constantRegex ,
567+ $ matchConstantString ,
568+ $ matchClassString ,
569+ $ class
444570 );
571+ } else {
572+ $ result = $ this ->checkNoAliasUseNamespace ($ matchConstantString , $ matchClassString , $ class );
573+ }
574+ } else {
575+ foreach ($ matchConstantString ['classWithConst ' ] as $ constantMatch ) {
576+ if (trim ($ constantMatch , '\\' ) === $ class ) {
577+ $ result = 1 ;
578+ break ;
579+ }
445580 }
581+
446582 }
447583 } else {
448- $ fullyQualified = $ constant ;
449- $ regex = preg_quote ($ constant , '/ ' );
584+ $ result = 1 ;
450585 }
451- $ this ->_assertNotRegExp (
452- '/[^a-z\d_]( ' . $ regex . ')[^a-z\d_]/iS ' ,
453- $ content ,
454- $ this ->_suggestReplacement (sprintf ("Constant '%s' is obsolete. " , $ fullyQualified ), $ replacement )
586+ }
587+ $ this ->assertSame (
588+ 0 ,
589+ $ result ,
590+ $ this ->_suggestReplacement (sprintf ("Constant '%s' is obsolete. " , $ constant ), $ replacement )
591+ );
592+ }
593+
594+ /**
595+ * Check proper usage of 'as' alias in 'use' or 'namespace' in context of constant
596+ *
597+ * @param string $constantRegex
598+ * @param string $matchConstantString
599+ * @param string $matchClassString
600+ * @param string $class
601+ * @return int
602+ */
603+ private function checkAliasUseNamespace (
604+ $ constantRegex ,
605+ $ matchConstantString ,
606+ $ matchClassString ,
607+ $ class
608+ ) {
609+ $ foundProperUse = false ;
610+ $ foundAsComponent = false ;
611+ $ asComponent = $ matchClassString ['classAlias ' ];
612+ foreach ($ matchConstantString ['constPart ' ] as $ constantMatch ) {
613+ $ expectedOnlyConst = '/ ' . $ asComponent . preg_quote (':: ' ) . $ constantRegex . '/ ' ;
614+ $ expectedConstPartialClass = '/ ' . $ asComponent . preg_quote ('\\' )
615+ . $ constantRegex . '/ ' ;
616+ if ((preg_match ($ expectedOnlyConst , $ constantMatch ) === 1 )
617+ || (preg_match ($ expectedConstPartialClass , $ constantMatch ) === 1 )) {
618+ $ foundAsComponent = true ;
619+ }
620+ if (strpos ($ constantMatch , ':: ' ) !== false ) {
621+ $ foundProperUse = $ this ->checkCompletePathOfClass (
622+ $ constantMatch ,
623+ $ matchClassString ,
624+ $ class ,
625+ $ foundAsComponent ,
626+ $ asComponent
627+ );
628+ if ($ foundProperUse ) {
629+ break ;
630+ }
631+ }
632+ }
633+ if ($ foundProperUse ) {
634+ return 1 ;
635+ } else {
636+ return 0 ;
637+ }
638+ }
639+
640+ /**
641+ * Check proper usage of classpath in constant and 'use'/'namespace' when there is no 'as' alias
642+ *
643+ * @param string $matchConstantString
644+ * @param string $matchClassString
645+ * @param string $class
646+ * @return int
647+ */
648+ private function checkNoAliasUseNamespace (
649+ $ matchConstantString ,
650+ $ matchClassString ,
651+ $ class
652+ ) {
653+ $ foundProperUse = false ;
654+ foreach ($ matchConstantString ['constPart ' ] as $ constantMatch ) {
655+ $ foundProperUse = $ this ->checkCompletePathOfClass (
656+ $ constantMatch ,
657+ $ matchClassString ,
658+ $ class
455659 );
660+ if ($ foundProperUse ) {
661+ break ;
662+ }
663+ }
664+ if ($ foundProperUse ) {
665+ return 1 ;
666+ } else {
667+ return 0 ;
668+ }
669+ }
670+
671+ /**
672+ * Check if class path with constant and in 'use' or 'namespace' forms complete classpath
673+ *
674+ * @param string $constantMatch
675+ * @param array $matchClassString
676+ * @param string $class
677+ * @param bool $foundAsComponent
678+ * @param string $asComponent
679+ * @return bool
680+ */
681+ private function checkCompletePathOfClass (
682+ $ constantMatch ,
683+ $ matchClassString ,
684+ $ class ,
685+ $ foundAsComponent = false ,
686+ $ asComponent = ''
687+ ) {
688+ $ temp = explode (':: ' , $ constantMatch );
689+ $ pathWithConst = trim (ltrim (str_replace ('\\\\' , '\\' , $ temp [0 ]), '\\' ));
690+ if ($ pathWithConst === $ class ) {
691+ return true ;
692+ }
693+ if ($ foundAsComponent ) {
694+ $ pathWithConst = ltrim (preg_replace ('/^ ' . $ asComponent . '/ ' , '' , $ pathWithConst ), '\\' );
695+ if ($ pathWithConst === '' ) {
696+ return true ;
697+ }
698+ }
699+ $ pathWithConstParts = explode ('\\' , $ pathWithConst );
700+ $ pathInUseNamespace = trim ($ matchClassString ['classPath ' ], '\\' );
701+ $ pathInUseNamespaceTruncated = trim (trim (
702+ preg_replace (
703+ '/ ' . preg_quote ($ pathWithConstParts [0 ]) . '$/ ' ,
704+ '' ,
705+ $ pathInUseNamespace
706+ ),
707+ '\\'
708+ ));
709+ if ($ this ->checkClasspathProperDivisionNoConstantPath (
710+ $ pathInUseNamespaceTruncated ,
711+ $ pathInUseNamespace ,
712+ $ matchClassString ,
713+ $ class ,
714+ $ foundAsComponent
715+ )) {
716+ return true ;
717+ } else {
718+ return $ this ->checkClasspathProperDivisionWithConstantPath (
719+ $ pathInUseNamespaceTruncated ,
720+ $ pathInUseNamespace ,
721+ $ pathWithConst ,
722+ $ class ,
723+ $ foundAsComponent
724+ );
725+ }
726+ }
727+
728+ /**
729+ * Check if classpath is divided in two places with correct constant name
730+ *
731+ * @param string $pathInUseNamespaceTruncated
732+ * @param string $pathInUseNamespace
733+ * @param array $matchClassString
734+ * @param string $class
735+ * @param bool $foundAsComponent
736+ * @return bool
737+ */
738+ private function checkClasspathProperDivisionNoConstantPath (
739+ $ pathInUseNamespaceTruncated ,
740+ $ pathInUseNamespace ,
741+ $ matchClassString ,
742+ $ class ,
743+ $ foundAsComponent
744+ ) {
745+ if ($ pathInUseNamespaceTruncated === $ pathInUseNamespace && $ pathInUseNamespaceTruncated !== $ class
746+ && ($ foundAsComponent || (strpos ($ matchClassString ['useOrNamespace ' ], 'namespace ' ) !== false ))) {
747+ return true ;
748+ } else {
749+ return false ;
750+ }
751+ }
752+
753+ /**
754+ * Check if classpath is divided in two places with constant properly with or without alias
755+ *
756+ * @param string $pathInUseNamespaceTruncated
757+ * @param string $pathInUseNamespace
758+ * @param string $pathWithConst
759+ * @param string $class
760+ * @param bool $foundAsComponent
761+ * @return bool
762+ */
763+ private function checkClasspathProperDivisionWithConstantPath (
764+ $ pathInUseNamespaceTruncated ,
765+ $ pathInUseNamespace ,
766+ $ pathWithConst ,
767+ $ class ,
768+ $ foundAsComponent
769+ ) {
770+ if ((($ pathInUseNamespaceTruncated . '\\' . $ pathWithConst === $ class )
771+ && ($ pathInUseNamespaceTruncated !== $ pathInUseNamespace ) && !$ foundAsComponent )
772+ || (($ pathInUseNamespaceTruncated === $ class ) && (strpos ($ pathWithConst , '\\' ) === false )
773+ && $ foundAsComponent )) {
774+ return true ;
775+ } else {
776+ return false ;
456777 }
457778 }
458779
@@ -465,7 +786,7 @@ protected function _testObsoleteConstants($content)
465786 */
466787 protected function _isClassConstantDefined ($ content , $ constant )
467788 {
468- return (bool )preg_match ('/ ' . $ this ->_getClassConstantDefinitionRegExp ($ constant ) . '/iS ' , $ content );
789+ return (bool )preg_match ('/ ' . $ this ->_getClassConstantDefinitionRegExp ($ constant ) . '/S ' , $ content );
469790 }
470791
471792 /**
0 commit comments