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