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 |
---|