| 1 | from django.conf import settings
|
|---|
| 2 | from django.template import TemplateDoesNotExist
|
|---|
| 3 | from django.template.loader import find_template_source
|
|---|
| 4 | from django.core.cache import cache
|
|---|
| 5 | from subprocess import Popen, PIPE
|
|---|
| 6 | import sys, os, threading
|
|---|
| 7 |
|
|---|
| 8 | def _getdefault(name, default=None):
|
|---|
| 9 | try:
|
|---|
| 10 | default = getattr(settings, name)
|
|---|
| 11 | except: pass
|
|---|
| 12 | return default
|
|---|
| 13 |
|
|---|
| 14 | ## these should be set in the settings.py file
|
|---|
| 15 | PHP_BIN = _getdefault('PHP_BIN', '/usr/bin/php')
|
|---|
| 16 | PHP_ARGS = _getdefault('PHP_ARGS', ['-q',])
|
|---|
| 17 | PHP_IN_SHELL = _getdefault('PHP_IN_SHELL', True)
|
|---|
| 18 | PHP_THREAD_IO = _getdefault('PHP_THREAD_IO', sys.platform == 'win32')
|
|---|
| 19 | PHP_BUFFER_SIZE = _getdefault('PHP_BUFFER_SIZE',
|
|---|
| 20 | sys.platform == 'win32' and -1 or 4096)
|
|---|
| 21 | PHP_CACHE_SECONDS = _getdefault('PHP_CACHE_SECONDS', None)
|
|---|
| 22 |
|
|---|
| 23 | class PipeThread(threading.Thread):
|
|---|
| 24 | """This is needed for Win32 where the buffer can lock if you
|
|---|
| 25 | pass the windows shell buffer size and do not read the output pending
|
|---|
| 26 | from the running app"""
|
|---|
| 27 | def __init__(self, fin, mode='read'):
|
|---|
| 28 | self.fin = fin
|
|---|
| 29 | self.sout = ""
|
|---|
| 30 | self.mode = mode
|
|---|
| 31 | threading.Thread.__init__(self)
|
|---|
| 32 | def run(self):
|
|---|
| 33 | if self.mode == 'read':
|
|---|
| 34 | self.sout = self.fin.read()
|
|---|
| 35 | else:
|
|---|
| 36 | self.fin.write(self.sout)
|
|---|
| 37 | def read(self):
|
|---|
| 38 | return self.sout
|
|---|
| 39 | def write(self, data):
|
|---|
| 40 | self.sout = data
|
|---|
| 41 | def close(self):
|
|---|
| 42 | ## WARNING! This can cause a crash on windows depending on the FD
|
|---|
| 43 | ## If its an stdout or stderr FD then this will crash
|
|---|
| 44 | if self.mode != 'read':
|
|---|
| 45 | self.fin.close()
|
|---|
| 46 |
|
|---|
| 47 | def runphp(source):
|
|---|
| 48 | php = Popen([PHP_BIN,] + PHP_ARGS, shell=PHP_IN_SHELL,
|
|---|
| 49 | bufsize=1<<12, universal_newlines=True, ## restriction of templates
|
|---|
| 50 | stdin=PIPE, stdout=PIPE, #stderr=PIPE,
|
|---|
| 51 | close_fds= sys.platform != 'win32' and True or False)
|
|---|
| 52 |
|
|---|
| 53 | ## RED_FLAG: check for returncode after reading stdout and if its an error
|
|---|
| 54 | ## (and we are TEMPLATE_DEBUG), then raise a special error with
|
|---|
| 55 | ## the contents of stderr... wee...
|
|---|
| 56 | if not PHP_THREAD_IO:
|
|---|
| 57 | php.stdin.write(source)
|
|---|
| 58 | php.stdin.close() # or we block on stdout.read()
|
|---|
| 59 | page = php.stdout.read()
|
|---|
| 60 | php.stdout.close() # just in case we are windows
|
|---|
| 61 | else:
|
|---|
| 62 | phpin = PipeThread(php.stdin, 'write')
|
|---|
| 63 | phpin.write(source)
|
|---|
| 64 | phpin.start()
|
|---|
| 65 | phpout = PipeThread(php.stdout)
|
|---|
| 66 | phpout.start()
|
|---|
| 67 | phpin.close()
|
|---|
| 68 | retcode = php.wait()
|
|---|
| 69 | phpout.join(1)
|
|---|
| 70 | page = phpout.read()
|
|---|
| 71 | return page
|
|---|
| 72 |
|
|---|
| 73 | def load_template_source(template_name, template_dirs=None):
|
|---|
| 74 | """all php template requests must start with 'php:' to keep namespaces
|
|---|
| 75 | distinct and as an added security measure
|
|---|
| 76 | (and it would be recursive otherwise)"""
|
|---|
| 77 | if len(template_name) <= 4 or template_name[:4] != 'php:':
|
|---|
| 78 | raise TemplateDoesNotExist, (
|
|---|
| 79 | "Not a PHP template request: %s" % template_name)
|
|---|
| 80 | if PHP_CACHE_SECONDS:
|
|---|
| 81 | page_and_origin_name = cache.get(template_name)
|
|---|
| 82 | if page_and_origin_name: return page_and_origin_name
|
|---|
| 83 | try:
|
|---|
| 84 | source, origin = find_template_source(template_name[4:], template_dirs)
|
|---|
| 85 | except TemplateDoesNotExist:
|
|---|
| 86 | raise TemplateDoesNotExist, template_name
|
|---|
| 87 |
|
|---|
| 88 | page = runphp(source)
|
|---|
| 89 |
|
|---|
| 90 | page_and_origin_name = (page, origin and origin.name or template_name)
|
|---|
| 91 | if PHP_CACHE_SECONDS:
|
|---|
| 92 | cache.set(template_name, page_and_origin_name, PHP_CACHE_SECONDS)
|
|---|
| 93 | return page_and_origin_name
|
|---|
| 94 | load_template_source.is_usable=True
|
|---|