Original by Stefan Walk.

Methods
Classes and Modules
Module CodeRay::Scanners::PHP::RE
Module CodeRay::Scanners::PHP::Words
Constants
KINDS_NOT_LOC = HTML::KINDS_NOT_LOC
Public Instance methods
reset_instance()
# File lib/coderay/scanners/php.rb, line 18
    def reset_instance
      super
      @html_scanner.reset
    end
scan_tokens(tokens, options)
# File lib/coderay/scanners/php.rb, line 227
    def scan_tokens tokens, options
      
      if check(RE::PHP_START) ||  # starts with <?
       (match?(/\s*<\S/) && exist?(RE::PHP_START)) || # starts with tag and contains <?
       exist?(RE::HTML_INDICATOR) ||
       check(/.{1,100}#{RE::PHP_START}/om)  # PHP start after max 100 chars
        # is HTML with embedded PHP, so start with HTML
        states = [:initial]
      else
        # is just PHP, so start with PHP surrounded by HTML
        states = [:initial, :php]
      end
      
      label_expected = true
      case_expected = false
      
      heredoc_delimiter = nil
      delimiter = nil
      modifier = nil
      
      until eos?
        
        match = nil
        kind = nil
        
        case states.last
        
        when :initial  # HTML
          if scan RE::PHP_START
            kind = :inline_delimiter
            label_expected = true
            states << :php
          else
            match = scan_until(/(?=#{RE::PHP_START})/o) || scan_until(/\z/)
            @html_scanner.tokenize match unless match.empty?
            next
          end
        
        when :php
          if match = scan(/\s+/)
            tokens << [match, :space]
            next
          
          elsif scan(%r! (?m: \/\* (?: .*? \*\/ | .* ) ) | (?://|\#) .*? (?=#{RE::PHP_END}|$) !xo)
            kind = :comment
          
          elsif match = scan(RE::IDENTIFIER)
            kind = Words::IDENT_KIND[match]
            if kind == :ident && label_expected && check(/:(?!:)/)
              kind = :label
              label_expected = true
            else
              label_expected = false
              if kind == :ident && match =~ /^[A-Z]/
                kind = :constant
              elsif kind == :reserved
                case match
                when 'class'
                  states << :class_expected
                when 'function'
                  states << :function_expected
                when 'case', 'default'
                  case_expected = true
                end
              elsif match == 'b' && check(/['"]/)  # binary string literal
                modifier = match
                next
              end
            end
          
          elsif scan(/(?:\d+\.\d*|\d*\.\d+)(?:e[-+]?\d+)?|\d+e[-+]?\d+/i)
            label_expected = false
            kind = :float
          
          elsif scan(/0x[0-9a-fA-F]+/)
            label_expected = false
            kind = :hex
          
          elsif scan(/\d+/)
            label_expected = false
            kind = :integer
          
          elsif scan(/'/)
            tokens << [:open, :string]
            if modifier
              tokens << [modifier, :modifier]
              modifier = nil
            end
            kind = :delimiter
            states.push :sqstring
          
          elsif match = scan(/["`]/)
            tokens << [:open, :string]
            if modifier
              tokens << [modifier, :modifier]
              modifier = nil
            end
            delimiter = match
            kind = :delimiter
            states.push :dqstring
          
          elsif match = scan(RE::VARIABLE)
            label_expected = false
            kind = Words::VARIABLE_KIND[match]
          
          elsif scan(/\{/)
            kind = :operator
            label_expected = true
            states.push :php
          
          elsif scan(/\}/)
            if states.size == 1
              kind = :error
            else
              states.pop
              if states.last.is_a?(::Array)
                delimiter = states.last[1]
                states[-1] = states.last[0]
                tokens << [matched, :delimiter]
                tokens << [:close, :inline]
                next
              else
                kind = :operator
                label_expected = true
              end
            end
          
          elsif scan(/@/)
            label_expected = false
            kind = :exception
          
          elsif scan RE::PHP_END
            kind = :inline_delimiter
            states = [:initial]
          
          elsif match = scan(/<<<(?:(#{RE::IDENTIFIER})|"(#{RE::IDENTIFIER})"|'(#{RE::IDENTIFIER})')/o)
            tokens << [:open, :string]
            warn 'heredoc in heredoc?' if heredoc_delimiter
            heredoc_delimiter = Regexp.escape(self[1] || self[2] || self[3])
            kind = :delimiter
            states.push self[3] ? :sqstring : :dqstring
            heredoc_delimiter = /#{heredoc_delimiter}(?=;?$)/
          
          elsif match = scan(/#{RE::OPERATOR}/o)
            label_expected = match == ';'
            if case_expected
              label_expected = true if match == ':'
              case_expected = false
            end
            kind = :operator
          
          else
            getch
            kind = :error
          
          end
        
        when :sqstring
          if scan(heredoc_delimiter ? /[^\\\n]+/ : /[^'\\]+/)
            kind = :content
          elsif !heredoc_delimiter && scan(/'/)
            tokens << [matched, :delimiter]
            tokens << [:close, :string]
            delimiter = nil
            label_expected = false
            states.pop
            next
          elsif heredoc_delimiter && match = scan(/\n/)
            kind = :content
            if scan heredoc_delimiter
              tokens << ["\n", :content]
              tokens << [matched, :delimiter]
              tokens << [:close, :string]
              heredoc_delimiter = nil
              label_expected = false
              states.pop
              next
            end
          elsif scan(heredoc_delimiter ? /\\\\/ : /\\[\\'\n]/)
            kind = :char
          elsif scan(/\\./m)
            kind = :content
          elsif scan(/\\/)
            kind = :error
          end
        
        when :dqstring
          if scan(heredoc_delimiter ? /[^${\\\n]+/ : (delimiter == '"' ? /[^"${\\]+/ : /[^`${\\]+/))
            kind = :content
          elsif !heredoc_delimiter && scan(delimiter == '"' ? /"/ : /`/)
            tokens << [matched, :delimiter]
            tokens << [:close, :string]
            delimiter = nil
            label_expected = false
            states.pop
            next
          elsif heredoc_delimiter && match = scan(/\n/)
            kind = :content
            if scan heredoc_delimiter
              tokens << ["\n", :content]
              tokens << [matched, :delimiter]
              tokens << [:close, :string]
              heredoc_delimiter = nil
              label_expected = false
              states.pop
              next
            end
          elsif scan(/\\(?:x[0-9A-Fa-f]{1,2}|[0-7]{1,3})/)
            kind = :char
          elsif scan(heredoc_delimiter ? /\\[nrtvf\\$]/ : (delimiter == '"' ? /\\[nrtvf\\$"]/ : /\\[nrtvf\\$`]/))
            kind = :char
          elsif scan(/\\./m)
            kind = :content
          elsif scan(/\\/)
            kind = :error
          elsif match = scan(/#{RE::VARIABLE}/o)
            kind = :local_variable
            if check(/\[#{RE::IDENTIFIER}\]/o)
              tokens << [:open, :inline]
              tokens << [match, :local_variable]
              tokens << [scan(/\[/), :operator]
              tokens << [scan(/#{RE::IDENTIFIER}/o), :ident]
              tokens << [scan(/\]/), :operator]
              tokens << [:close, :inline]
              next
            elsif check(/\[/)
              match << scan(/\[['"]?#{RE::IDENTIFIER}?['"]?\]?/o)
              kind = :error
            elsif check(/->#{RE::IDENTIFIER}/o)
              tokens << [:open, :inline]
              tokens << [match, :local_variable]
              tokens << [scan(/->/), :operator]
              tokens << [scan(/#{RE::IDENTIFIER}/o), :ident]
              tokens << [:close, :inline]
              next
            elsif check(/->/)
              match << scan(/->/)
              kind = :error
            end
          elsif match = scan(/\{/)
            if check(/\$/)
              kind = :delimiter
              states[-1] = [states.last, delimiter]
              delimiter = nil
              states.push :php
              tokens << [:open, :inline]
            else
              kind = :string
            end
          elsif scan(/\$\{#{RE::IDENTIFIER}\}/o)
            kind = :local_variable
          elsif scan(/\$/)
            kind = :content
          end
        
        when :class_expected
          if scan(/\s+/)
            kind = :space
          elsif match = scan(/#{RE::IDENTIFIER}/o)
            kind = :class
            states.pop
          else
            states.pop
            next
          end
        
        when :function_expected
          if scan(/\s+/)
            kind = :space
          elsif scan(/&/)
            kind = :operator
          elsif match = scan(/#{RE::IDENTIFIER}/o)
            kind = :function
            states.pop
          else
            states.pop
            next
          end
        
        else
          raise_inspect 'Unknown state!', tokens, states
        end
        
        match ||= matched
        if $CODERAY_DEBUG and not kind
          raise_inspect 'Error token %p in line %d' %
            [[match, kind], line], tokens, states
        end
        raise_inspect 'Empty token', tokens, states unless match
        
        tokens << [match, kind]
        
      end
      
      tokens
    end
setup()
# File lib/coderay/scanners/php.rb, line 14
    def setup
      @html_scanner = CodeRay.scanner :html, :tokens => @tokens, :keep_tokens => true, :keep_state => true
    end