Научная статья на тему 'Применение метода токенизации для поиска схожих фрагментов кода в студенческих работах'

Применение метода токенизации для поиска схожих фрагментов кода в студенческих работах Текст научной статьи по специальности «Компьютерные и информационные науки»

CC BY
1545
281
i Надоели баннеры? Вы всегда можете отключить рекламу.
Ключевые слова
ПЛАГИАТ В ИСХОДНЫХ КОДАХ / МЕТОД ТОКЕНИЗАЦИИ / КОЭФФИЦИЕНТ ЖАККАРА / SOURCE-CODE PLAGIARISM / TOKEN-BASED APPROACH / JACCARD INDEX

Аннотация научной статьи по компьютерным и информационным наукам, автор научной работы — Шиков Алексей Николаевич, Сорокин Дмитрий Сергеевич

В статье рассматривается подход, основанный на методе токенизации и алгоритме нахождения наибольшей общей последовательности, для обнаружения дублирующих фрагментов кода в студенческих работах. Даются общие рекомендации по использованию метода токенизации.

i Надоели баннеры? Вы всегда можете отключить рекламу.
iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

In article the approach based on a method of tokenization and algorithm of finding of the greatest general sequence, for detection of duplicating fragments of a code in student"s works is considered. The general recommendations about use of a method of tokenization are made.

Текст научной работы на тему «Применение метода токенизации для поиска схожих фрагментов кода в студенческих работах»

Применение метода токенизации для поиска схожих фрагментов кода в студенческих работах

Шиков Алексей Николаевич к.т.н., доцент кафедры интеллектуальных технологий в гуманитарной сфере, Санкт-Петербургский национально исследовательский университет информационных технологий, механики и оптики,

Кронверкский проспект, д. 49, Санкт-Петербург, 197101, (812)2328645

shik-off@ma.i1.ni

Сорокин Дмитрий Сергеевич магистрант кафедры интеллектуальных технологий в гуманитарной сфере, Санкт-Петербургский национально исследовательский университет информационных технологий, механики и оптики, естественнонаучный факультет, Кронверкский проспект, д. 49, Санкт-Петербург, 197101, (812)2328645 Dementiv@vandex.nu

Аннотация

В статье рассматривается подход, основанный на методе токенизации и алгоритме нахождения наибольшей общей последовательности, для обнаружения дублирующих фрагментов кода в студенческих работах. Даются общие рекомендации по использованию метода токенизации.

In article the approach based on a method of tokenization and algorithm of finding of the greatest general sequence, for detection of duplicating fragments of a code in student's works is considered. The general recommendations about use of a method of tokenization are made.

Ключевые слова

плагиат в исходных кодах; метод токенизации; коэффициент Жаккара. source-code plagiarism; token-based approach; Jaccard index.

Введение

Студенческий плагиат является одной из серьезных проблем образовательных учреждений [1] (эта проблема не обошла стороной и массовые открытые онлайн курсы [2]) и в первую очередь это касается преподавателей тех дисциплин, на которых требуется писать программный код для выполнения заданий. При этом сами студенты не всегда осознают, какой ущерб они наносят своим собственным знаниям и всему процессу их дальнейшего обучения, используя чужие фрагменты кода в своих работах. Можно сказать, что не усвоив курс программирования на ранних этапах обучения студент вероятней всего начнет «проваливаться» по соответствующим дисциплинам и на последующих курсах. Кроме непонимания важности самостоятельного выполнения работ можно выделить широкий спектр других причин, по которым студенты занимаются плагиатом, среди которых: отсутствие мотивации, теоретических и практических знаний, времени, а также, совместная работа над выполнением задания [3, 4]. Еще одним важным фактором является то, что студенты иногда и вовсе не понимают значение термина «плагиат» [5].

Плагиат в программном коде определяют как «воспроизведение (копирование) исходного кода без внесения каких-либо изменений или с внесением, но незначительных изменений» [6]. Чтобы понять, что подразумевается под

незначительными изменениями, следует рассмотреть, какие изменения вообще возможны в программном коде.

Выделяют четыре типа дублирующих фрагментов кода (типы «клонов») [7]:

• 1 тип - два фрагмента кода являются идентичными, за исключением изменений в пробельных символах, отступах и комментариях;

• 2 тип - два фрагмента кода являются синтаксически идентичными, за исключением имен идентификаторов, строковых литералов, типов переменных и всех изменений, свойственных 1 типу;

• 3 тип - один фрагмент кода был получен путем копирования другого фрагмента кода, но последующим добавлением и/или удалением операторов языка. При этом сохраняются все изменения, свойственные 2 типу;

• 4 тип - два фрагмента синтаксически реализованы по-разному, но выполняют одинаковые вычисления.

Исходя из приведенной классификации, можно сказать, что к незначительным изменениям относятся 1, 2 и, возможно, 3 типы дублирующих фрагментов кода. В студенческих работах достаточно часто можно встретить изменения 1 и 2 типа, гораздо реже 3 и 4.

Методы и программы обнаружения плагиата

Методам обнаружения дублирующих фрагментов кода и программам-детекторам было посвящено множество исследований. Среди методов обнаружения наиболее распространенными являются [8]:

• String-based - происходит поиск точных строковых совпадений. Данный подход является очень чувствительным к изменению имен идентификаторов;

• Token-based - предварительно исходный код программы транслируется в последовательность токенов, а затем применяется один из алгоритмов строкового сравнения для поиска дублирующих фрагментов кода;

• Tree-based - для каждого файла с исходным кодом строится абстрактное синтаксическое дерево, затем полученные деревья сравниваются между собой [9];

• PDG-based (PDG - Program Dependency Graph) - строится граф зависимостей, который отражает зависимость между управляющими конструкциями программы (control dependency), т.е. как переходит управление из одной точки программы в другую, и между потоками данных (data dependency). Затем полученные графы для каждого файла с исходным кодом сравниваются между собой [10];

• Metrics-based - данный подход заключается в оценке метрик программы, например, количестве используемых переменных, циклов, условных операторов и т.д. Далее две программы сравниваются по соответствующим метрикам;

• Hybrid-based - гибридные подходы основаны на сочетание нескольких из выше перечисленных подходов.

Каждый из перечисленных методов имеет свои характеристики. Наиболее важной характеристикой является ответ на вопрос: «Какого типа дублирующие фрагменты кода способен обнаруживать выбранный метод?». Например, методы, основанные на сравнении строк, способны обнаруживать дублирующие фрагменты кода 1-го типа, методы, основанные на токенизации и построении абстрактных синтаксических деревьев, работают для 1 и 2-го типов, методы, основанные на построении графов зависимостей, работают для 1, 2 и 3 типов, методы, основанные на метриках, практически не используются в настоящий момент времени, так как имеют слишком высокую вероятность ошибки.

Программы-детекторы обычно реализуют один из описанных выше методов. При этом существуют как коммерческие решения, так и бесплатные. Сравнению и анализу программ-детекторов уделяется достаточно много внимания в различных исследованиях [11, 12]. Одними из наиболее доступных и распространенных детекторов являются:

• SIM - программа с открытым исходным кодом, используемая «Амстердамским свободным университетом», для тестирования лексического сходства в исходных кодах программ и обычных текстах [13];

• MOSS - веб-сервис для обнаружения плагиата в исходных кодах, используемый «Университетом им. Леланда Стэнфорда» (Стэнфордский университет) [14];

• JPlag - веб-сервис для обнаружения похожих фрагментов кода в представленном наборе файлов, используемый «Технологическим институтом Карлсруэ» [15];

• CCFinderX - детектор «клонов», используемый «Японским национальным институтом перспективной промышленной науки и технологии», для поиска и анализа дублирующих фрагментов кода в заданном наборе файлов [16].

Разработка детектора плагиата и описание алгоритма обнаружения

Разработка собственного детектора может быть обусловлена рядом причин, среди которых:

• существующие решения на бесплатной основе могут не поддерживать требуемый язык программирования, например, ни один из приведенных ранее детекторов не поддерживает язык программирования PHP, который широко используется в студенческих заданиях по программированию;

• отсутствие репозитория работ, то есть не сохраняются сведения об уже проверенных работах (например, чтобы их использовать в последующие годы);

• отсутствие веб-интерфейса, через который студенты могли бы загружать свои работы и получать сведения о результатах сравнения.

Для разрабатываемого детектора был выбран метод основанный на токенизации. Хотя данный метод и предназначен для обнаружения дублирующих фрагментов кода 1 и 2-го типов, он является легко расширяемым при введении новых языков.

При разработке детектора следует учитывать этапы, через которые обычно проходит программа при обнаружении совпадений [8]:

• этап подготовки (нормализации) исходного кода - данный этап заключается в подготовке файла с исходным кодом к дальнейшим преобразованиям путем удаления лишних пробельных символов, комментариев, строковых литералов, малоинтересных фрагментов кода (например, по заранее заданному шаблону) и т.п.;

• этап преобразований - после того как исходный код был нормализован, происходит одно или несколько его преобразований в зависимости от выбранной техники (например, получение последовательности токенов или построение абстрактного синтаксического дерева);

• этап обнаружения совпадений - по преобразованному коду производится поиск совпадений;

• этап восстановления (форматирования) - на данном этапе происходит сопоставление оригинальных фрагментов кода найденным совпадениям;

• этап представления результатов - представление результатов поиска совпадений, например, процент схожести двух файлов, подсветка соответствующих участков кода и построение различных графов [17].

Далее рассмотрен пример работы алгоритма обнаружения с использованием техники токенизации на синтетическом примере.

Представим, что есть два файла с исходным кодом на языке программирования С, в которых требуется найти дублирующие фрагменты кода:

fact.c

int fact = 1; int n = 5;

for (int i = n; i > 1; i--) fact = fact * i;

prod.c

int prod = 1; int m = 5; for (int j = 1;

j <= m;

j++)

prod = j * prod;

Первым шагом является получение последовательности токенов для каждого из файлов. Отдельный токен может представлять собой: ключевое слово языка, оператор, разделитель (например, фигурные или круглые скобки), строковый литерал и т.д. Обычно каждому токену сопоставляется либо численное значение, либо символьное (как в приведенном ниже примере). В примере будем использовать четыре типа токенов, соответствующих: ключевым словам языка (К),

идентификаторам (I), операторам языка (О) и числовым значениям (М).

Файл с исходным кодом: Последовательность токенов:

fact.c:

int fact = 1; K I O N

int n = 5; K I O N

for (int i = n; i > 1; i--) K K I O I I O N I O

fact = fact * i; I O I O I

prod.c:

int prod = 1; int m = 5; for (int j = 1;

j <= m; j++)

prod = j * prod;

K I O N K I O N K K I O N

I O I I O

I O I O I

Следующим шагом является вычисление коэффициента Жаккара (Jaccard Index), который является численной оценкой схожести двух файлов. Предварительно требуется по получившейся последовательности токенов сгенерировать N-граммы. N-грамма это последовательность из n элементов (размером n). Размер N-грамм следует выбирать в зависимости от размера файла с исходным кодом и количества уникальных токенов. Обычно размер N-грамм выбирают равным 4 [18]. N-граммы генерируются со сдвигом на один элемент.

Для простоты восприятия сопоставим каждому токену числовое значение, так для файла «fact.c» получившаяся последовательность чисел будет выглядеть следующим образом:

K I O N K I O N K K I O I I O N I O I O I O I

1 2 3 4 1 2 3 4 1 1 2 3 2 2 3 4 2 3 2 3 2 3 2

Теперь сгенерируем последовательности 4-грамм для файла «fact.c»:

1 2 З 4 1 2 З 4 1 1 2 З 2 2 З 4 2 З 2 З 2 З 2

N-граммы:

Уникальные N-граммы:

1 2 З 4 2 З 4 1 З 4 1 2

2 З 4 1

3 4 1 2

4 1 2 З

1 2 З 4

4 1 2 З 1 2 З 4 2 З 4 1 З 4 1 1

З 4 1 1

Слева показаны все получившиеся М-граммы (иногда считают частоту одинаковых М-грамм [19], т.е. сколько раз конкретная М-грамма встретилась в общей последовательности М-грамм), справа только уникальные.

Аналогичная операция производится и для файла «ргоіс». Таким образом, каждая программа представлена последовательностью 4-грамм.

Теперь получившиеся последовательности М-грамм могут быть использованы для оценки схожести двух файлов по формуле Жаккара [20]:

где А и В это количество уникальных 4-грамм в файла «Гай.с» и «рі^.с», соответственно. Таким образом, коэффициент Жаккара равен:

Если сравнивается множество файлов, то на данном этапе может быть отсеяна часть пар файлов из дальнейшего рассмотрения, процент схожести которых не превысил некоторого заранее установленного значения. Также следует заметить, что полученная оценка схожести является приблизительной и не может трактоваться как точная.

Следующим этапом в работе программы является формирование пар дублирующих фрагментов кода (clone pairs). Для этого используется алгоритм нахождения наибольшей общей подстроки (Longest Common Substring) на полученных ранее последовательностях токенов [21].

На каждом шаге находится наибольшая общая подстрока, но не менее длины N. Совпавшие последовательности токенов объединяются в пары дублирующих фрагментов кода и исключаются из дальнейшего рассмотрения.

1 шаг:

fact.c: K I O N K I O N K K I O I I O N I O I O I O I prod.c: K I O N K I O N K K I O N I O I I O I O I O I

2 шаг:

fact.c: I I O N I O I O I O I prod.c: N I O I I O I O I O I

3 шаг:

fact.c: I I O N

prod.c: N I O I

В ходе применения LCS для последовательностей токенов было найдено две пары дублирующих фрагментов кода (на 1 и 2 шаге, соответственно), по которым требуется провести восстановление. Для этого необходимо каждой найденной паре сопоставить соответствующие позиции в оригинальных файлах. Решением может служить сохранение позиции в файле для каждого токена на этапе токенизации. Тогда в каждую пару достаточно включать не всю последовательность совпавших токенов, а только первый и последний токены, из которых затем можно извлечь соответствующие им позиции в файле с исходным кодом.

iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

int fact = 1; int prod = 1;

int n = 5; int m = 5;

for (int i = n; i > 1; i--) for (int j = 1;

fact = fact * i; j <= m;

Методы с использованием токенизации очевидно не являются идеальными, например, рассмотрим простой пример:

Очевидно, что оба файла являются идентичными за исключением одной неиспользуемой переменной «unused_var». При этом ни один из описанных ранее детекторов не может определить, что файлы идентичны. Связанно это с тем, что техника токенизации очень чувствительна к добавлению новых операторов. Решением данной проблемы может служить использование статического анализатора кода, который может найти неиспользуемые переменные и функции. Например, для языка программирования С может быть использован cppcheck [22] :

$ cppcheck --enable=style fact_copy.c Checking fact_copy.c...

[fact_copy.c:7]: (style) Unused variable: unused_var

Checking usage of global fonctions..

Поиск и очистка файла от неиспользуемых переменных и функций может производиться на этапе нормализации файлов с исходным кодом. Анализатор кода может использоваться и для случаев MOOC, где работы оцениваются автогрейдерами. В таких случаях, если код забит неиспользуемыми переменными и функциями, детектор плагиата может его пропустить.

j++)

prod = j * prod;

fact_orig.c:

int main()

{

int fact = 1; int n = 5;

for (int i = n; i > 1; i--) fact = fact * i; printf(“%d\n”, fact); return 0;

}

int fact = 1;

int n = 5; int unused_var; for (int i = n; i > 1; i--) fact = fact * i;

fact_copy.c:

int main()

{

prinf(“%d\n”, fact); return 0;

}

42З

Для других языков программирования также существуют анализаторы кода, например для PHP можно использовать PHP Mess Detector [23], для Java и JavaScript существует PMD [24].

Проведение экспериментов

Для проведения экспериментов было собрано 80 работ студентов по дисциплине «Объектно-ориентированное программирование на С++» за осенний семестр 2012 и 2013 годов. Каждому студенту выдавался номер варианта работы, так чтобы у разных студентов варианты работ между собой не пересекались (пересечения возможны только между группами).

Все 80 работ были проверены на совпадения разрабатываемым детектором плагиата. Результат работы детектора на настоящий момент состоит из коэффициента Жаккара и веб-страницы, формируемой по результатам найденных совпадений.

Работы с коэффициентом Жаккара ниже 80% автоматически отсеивались. После отсеивания осталось 14 пар работ. Из которых 2 пары работ были ложно включены в отчет из-за высокого коэффициента Жаккара, который составлял около 85%. В оставшихся 12 парах работ были очевидны заимствования кода (примеры см. на рис. 1 - 3), из которых 3 пары работ дали 100% совпадение и 2 пары более 90%.

Рис. 1. Изменены имена идентификаторов у формальных параметров функции

>id sortlgr (short int +np, scan_info *tabl, FILE *fi)

fseek (fi, 2, SEEK_SET); scan_info *info = new scan_info [*np]; fread (info, sizeof(scan_info), *np, fi); scan_info *min = new scan_info [*np];

for (int j = *np-i; j > 0; j—) for (i = 0; i < j; i++)

{

if (info[i+l] .grey < info[i] .grey) f

min[i].grey = info[i] .grey; info[i].grey = info[i+l] .grey; info[i+l].grey = min[i].grey; fwrite(info, sizeof(scan_info), *np, fi);

}

for (i - O; i < *np; i++)

I

cout « "Mote ” « i « endl;

cout « "Name of model " « info[i] .model « endl;

cout « "price " « info[i] .price « endl;

cout « "Horisontal scon size is " « info[i] .x_size «

endl;

cout « "Vertical scan size is " « info[i] .y_size « rendl:___________________________________________________________________________

47 void sortlgr (int *np, scan_info *tabl, FILE *fi)

¡E {

fseek (fi, 2, SEEK_SET); scan_info *info = new scan_info [*np]; fread (info, sizeof(scan_info), *np, fi); scan_info *min = new scan_info [*np]; int i = 0;

for (int j = *np - 1; j > 0; j—)

for (i = 0; i < j; i++) f

if (info[i+l] .gray < info[i] .gray)

min[i].gray = info[i].gray; info[i].gray = info[i+l].gray; info[i+l] .gray = min[i] .gray; fwrite(info, sizeaf(scan_info), *np, fi);

>

for (i = 0; i < *np; i++)

"1Лия" « info[i] .model « endl;

"Цена" « info[i].price « endl;

"По горизонтали" « info[i].x_size « endl; ”По вертикали" « info[i].y_size « endl; "Разрешение" « info[i].optr « endl; "Градации серого " « info[i] .gray « endl;

cout « 1 cout « ' cout ■> cout « 1 cout « ' cout « '

_L_

Рис. 2. В некоторых местах изменены имена идентификаторов и форматирование кода

Рис. 3. Пример функционального изменения

Для двух работ, которые дали ложное совпадение, был увеличен размер N грамм (с 4 до 5), после чего коэффициент Жаккара для них составил около 65%. Отсюда следует, что возможны ошибки первого и второго рода. Ошибки первого рода (ложная тревога) возможны в тех случаях, когда два файла имеют схожую синтаксическую структуру. Ошибки второго рода (пропуск события) связаны с добавлением или удалением операторов.

Выводы и перспективы

Как было сказано в начале статьи, плагиат в исходных кодах работ студентов является одной из серьезных проблем образовательных учреждений, что подтверждается результатами экспериментов. Детекторы плагиата уже применяются

большинством крупных западных университетов, среди которых: Массачусетский Технологический Институт, Стэнфордский университет, Калифорнийский университет в Беркли, Гарвардский университет и многие другие. При этом не стоит забывать, что нужно полагаться не только на детектор плагиата, но и повышать личную ответственность студентов, разъясняя им последствия заимствования чужих работ.

Дальнейшие планы по разработке и улучшению детектора заключаются в введении новых метрик для процентной оценки схожести двух файлов, так как коэффициент Жаккара является достаточно приблизительным. Также планируется улучшить метод токенизации для обнаружения совпадений 3 -го типа.

Литература

1. Butterfield, F., Scandal over cheating at MIT stirs debate on limits of teamwork // The New York Times, (May 22, 1991), A23.

2. Jeffrey R. Young, Dozens of Plagiarism Incidents Are Reported in Coursera's Free Online Courses // The Chronicle of Higher Education, 16 August 2012.

3. Neal R. Wagner, Plagiarism by Student Programmers // The University of Texas at San Antonio Division Computer Science San Antonio, TX 78249, USA Copyright 2000.

4. Fredrik Gerhardus Hattingh, Albertus A. K. Buitendag, Jacobus S. Van Der Walt, Presenting an Alternative Source Code Plagiarism Detection Framework for Improving the Teaching and Learning of Programming // Journal of Information Technology Education: Innovations in Practice Volume 12, 2013.

5. Joy, M.S., Sinclair, J.E., Boyatt, R.C., Yau, J.Y-K. and Cosma, G., Student Perspectives on Plagiarism in Computing // In: 5th International Plagiarism Conference, 16-18 July 2012, Newcastle, UK.

6. D. Pawelczak, Online detection of source - code plagiarism in undergraduate programming courses // FECS'13: The 9th International Conference on Frontiers in Education: Computer Science and Computer Engineering, part of Worldcomp'13, July 22-25, 2013, Las Vegas, USA, S. 57-63.

7. Chanchal K. Roy, James R. Cordy, Rainer Koschke, Comparison and Evaluation of Code Clone Detection Techniques and Tools: A Qualitative Approach // Science of Computer Programming Volume 74, Issue 7, 1 May 2009, Pages 470-495.

8. Chanchal Kumar Roy and James R. Cordy, A Survey on Software Clone Detection Research // Technical Report No. 2007-541, September 2007.

9. Ira D. Baxter, Andrew Yahin, Leonardo Moura, Marcelo Sant’Anna, Lorraine Bier, Clone Detection Using Abstract Syntax Trees // Proc. Int'l Conf. Software Maintenance, 1998.

10. Chao Liu, Chen Chen , Jiawei Han , Philip S. Yu, GPLAG: Detection of Software Plagiarism by Program Dependence Graph Analysis // Proceedings of the 12th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining, 2006, Pages: 872-881.

11. Лифшиц Ю., Антипов Д., Ефтифеева О., Котов А., Красс А., Лакунин М., Лысенко Е., Семенников А., Счастливцев Р. Обзор автоматических детекторов плагиата в программах. URL: _ http://logic.pdmi.ras.ru/~yura/detector/survey.pdf (дата обращения: 01.02.2014).

12. Jurriaan Hage, Peter Rademaker, Nike van Vugt, A comparison of plagiarism detection tools // Technical Report UU-CS-2010-015, June 2010, Department of Information and Computing Sciences Utrecht University, Utrecht, The Netherlands.

13. Программа поиска совпадений в обычных и программных текстах SIM [Офиц. сайт]. URL: http://dickgrune.com/Programs/similarity_tester/ (дата обращения:

01.02.2014)

14. Программа обнаружения плагиата в программных текстах MOSS [Офиц. сайт]. URL: http://theory.stanford.edu/~aiken/moss/ (дата обращения: 01.02.2014)

15. Программа поиска дублирующих фрагментов кода JPlag [Офиц. сайт]. URL: https://jplag.ipd.kit.edu/ (дата обращения: 01.02.2014)

16. Детектор дублирующих фрагментов кода CCFinderX [Офиц. сайт]. URL: http://www.ccfinder.net/ (дата обращения: 01.02.2014)

17. Zhen Ming Jiang, Visualizing and Understanding Code Duplication in Large Software Systems // Master’s Thesis, The David R. Cheriton School of Computer Science, Faculty of Mathematics, University of Waterloo, Ontario, Canada. 2006.

18. Steven Burrows, Seyed M. M. Tahaghoghi, and Justin Zobel, Efficient and effective plagiarism detection for large code repositories // Proceedings of the Second Australian Undergraduate Students’ Computing Conference (AUSCC04), pages 8-15, 2004.

19. Ameera Jadalla, Ashraf Elnagar, PDE4Java: Plagiarism Detection Engine for Java source code: a clustering approach // International Journal of Business Intelligence and Data Mining, Volume 3, Number 2, 28 September 2008 , pp. 121-135(15).

20. Коэффициент Жаккара [Электронный ресурс] // Свободная электронная энциклопедия [Офиц. сайт]. URL: http://en.wikipedia.org/wiki/Jaccard_index (дата обращения: 01.02.2014)

21. Проблема поиска наибольшей общей подстроки [Электронный ресурс] // Свободная электронная энциклопедия [Офиц. сайт]. URL: http://en.wikipedia.org/wiki/Longest_common_substring_problem (дата обращения:

01.02.2014)

22. Инструмент статического анализа исходного кода на языках С/С++ Cppcheck [Офиц. сайт]. URL: http://cppcheck.sourceforge.net/ (дата обращения: 01.02.2014)

23. Инструмент статического анализа исходного кода на языке PHP PHPMD [Офиц. сайт]. URL: http://phpmd.org/ (дата обращения: 01.02.2014)

24. Инструмент статического анализа исходного кода на языках Java/Javascript [Офиц. сайт]. URL: http://pmd.sourceforge.net/ (дата обращения: 01.02.2014)

i Надоели баннеры? Вы всегда можете отключить рекламу.