There's a much simpler solution. If you want to just stop the transition (and not pause it). Just set the css to current computed style. For example in a custom scrolling solution, where top is transitioned property.
// 重設(style)屬性
element.style.top=getComputedStyle(element).top;
2016年12月22日 星期四
2016年12月21日 星期三
(js)jq.slide會控制的地方
display: inline-block;
overflow: hidden;
height: 101.269px;<animate>
padding-top: 44.8092px;<animate>
padding-bottom: 44.8092px;<animate>
margin-top: 0px;
margin-bottom: 0px
inline元素視為inline-block
inline只能透過(font-size)(padding)修改高度
overflow: hidden;
height: 101.269px;<animate>
padding-top: 44.8092px;<animate>
padding-bottom: 44.8092px;<animate>
margin-top: 0px;
margin-bottom: 0px
inline元素視為inline-block
inline只能透過(font-size)(padding)修改高度
(css)box-sizing的問題
摘要
初始值 | content-box |
---|---|
適用元素 | all elements that accept width or height |
是否是繼承屬性 | 否 |
適用媒體 | visual |
計算值 | as specified |
Animation type | discrete |
正規順序 | the unique non-ambiguous order defined by the formal grammar |
語法
如何閱讀 CSS 語法。content-box | border-box
/* 關鍵字值 */
box-sizing: content-box;
box-sizing: border-box;
/* 全局值 */
box-sizing: inherit;
box-sizing: initial;
box-sizing: unset;
值
content-box
- 默認值,標準盒模型。
width
與height
只包括內容的寬和高, 不包括邊框(border),內邊距(padding),外邊距(margin)。注意: 內邊距, 邊框 & 外邊距 都在這個盒子的外部。 比如. 如果 .box {width: 350px}; 而且 {border: 10px solid black;} 那麼在瀏覽器中的渲染的實際寬度將是370px;
尺寸計算公式:width = 內容的寬度,height = 內容的高度。寬度和高度都不包含內容的邊框(border)和內邊距(padding)。 border-box
-
width
與height
包括內邊距(padding)與邊框(border),不包括外邊距(margin)。這是IE 怪異模式(Quirks mode)使用的 盒模型 。注意:這個時候內邊距和邊框將會包括在盒子中。比如.box {width: 350px; border: 10px solid black;} 瀏覽器渲染出的寬度將是350px
. 如果計算後的最內部的內容寬度為負值,都會被計算成0,內容還在,所以不可能通過border-box來隱藏元素。
尺寸計算公式:width = border + padding + 內容的寬度,height = border + padding + 內容的高度。 正式的語法
content-box | border-box
例子
/* 支持 Firefox, Chrome, Safari, Opera, IE8+ 和老的Android瀏覽器 */
.example {
-moz-box-sizing: border-box;
box-sizing: border-box;
}
(css)(行內)與(position:absolute)的特性
行內的特性
行內只能靠 font-size, padding調整高度
line-height 無法作用
test
test
行內的size會被父親的大小給影響
一旦賦予(absolute)就會變成(block)
block一旦設定(position:absolute)若沒設定寬度
寬度就會變成(inline-block)的特性
(css)類選擇器對nth-child(1)無用的狀況
<section class="featured video">
<h1>VIDEO</h1>
</section>
<section class="featured module">
<h1>NOT A VIDEO</h1>
</section>
<section class="featured module">
<h1>NOT A VIDEO</h1>
</section>
<section class="featured module">
<h1>NOT A VIDEO (3)</h1>
</section>
<section class="featured module">
<h1>NOT A VIDEO</h1>
</section>
<section class="featured module">
<h1>NOT A VIDEO</h1>
</section>
<section class="featured module">
<h1>NOT A VIDEO (6)</h1>
</section>
section.module:nth-child(1) => 會選到 <section class="featured video">
class無用
(dom)一些功能整理
// 抓取css屬性
css = dom.currentStyle || window.getComputedStyle(dom, null);
style = dom.style;
原本 css.display = 'inline;
js 設定 style.display = 'inline-block';
css.display = 'inline-block';
// 抓取長度寬度(border + padding + 內容)
var rect = dom.getBoundingClientRect();
dom.offsetHeight
dom.offsetWidth
// 抓取長度寬度(padding + 內容)
dom.clientWidth
dom.clientHeight
css = dom.currentStyle || window.getComputedStyle(dom, null);
style = dom.style;
原本 css.display = 'inline;
js 設定 style.display = 'inline-block';
css.display = 'inline-block';
// 抓取長度寬度(border + padding + 內容)
var rect = dom.getBoundingClientRect();
dom.offsetHeight
dom.offsetWidth
// 抓取長度寬度(padding + 內容)
dom.clientWidth
dom.clientHeight
2016年12月20日 星期二
(js)deepCopy
module.exports = deepCopy;
/**
* 可複製3種格式({}, [], Map)
*
* g8
*/
function deepCopy(data) {
// debugger;
var toString = Object.prototype.toString;
var clone;
var type = _getType(data);
// 若(data)是(不含子孫物件)
if (data == null || (type != 'array' && type != 'map' && !_isPlainObject(data))) {
clone = data;
return clone;
}
/* ---------------------------------- */
// data含有子孫物件
if (type == 'map') {
clone = new Map();
} else if (type == 'array') {
clone = [];
} else {
clone = {};
}
copyChild(clone, data);
//////////////////////////////////////////////////
/**
* 會進來的(data)一定是有子孫元素
*/
function copyChild(target, data) {
// debugger;
var clone;
var dataType = _getType(data);
var tar_getType = _getType(target);
/* ---------------------------------- */
if (tar_getType != dataType) {
throw new Error('要拷貝的數據型態不同');
} else if (dataType == 'map') {
// 若是(Map)
data.forEach(function(child, key) {
var childType = _getType(child);
if (childType != 'array' && childType != 'map' && !_isPlainObject(child)) {
// 若子物件是單純數據
target.set(key, child);
} else {
// 若(child)物件還攜帶有子孫
// (clone)必須與(child)同型態
clone = _getInitClone(childType);
/* ------------------------ */
// 遞回,把child拷貝到 clone
copyChild(clone, child);
target.set(key, clone);
}
});
} else if (dataType == 'array') {
// 若是(array)
data.forEach(function(child, key) {
var childType = _getType(child);
if (childType != 'array' && childType != 'map' && !_isPlainObject(child)) {
// 若子物件是單純數據
target[key] = child;
} else {
// 若子物件還攜帶有子孫
// (clone)必須與(child)同型態
clone = _getInitClone(childType);
/* ------------------------ */
// 遞回,把child拷貝到 clone
copyChild(clone, child);
target[key] = clone;
}
});
} else if (_isPlainObject(data)) {
// 若(data)是(PlainObject)
for (var key in data) {
if (data.hasOwnProperty(key)) {
var child = data[key];
var childType = _getType(child);
if (childType != 'array' && childType != 'map' && !_isPlainObject(child)) {
// 若子物件是單純數據
target[key] = child;
} else {
// 若子物件還攜帶有子孫
// (clone)必須與(child)同型態
clone = _getInitClone(childType);
/* ------------------------ */
// 遞回,把child拷貝到 clone
copyChild(clone, child);
target[key] = clone;
}
}
}
} else {
throw new Error('data have no child');
}
};
/* ---------------------------------- */
return clone;
////////////////////////////////////////////////////////////////////////////
function _getInitClone(type) {
var clone;
switch (type) {
case 'array':
clone = [];
break;
case 'map':
clone = new Map();
break;
default:
clone = {};
break;
}
return clone;
};
/* ====================================================================== */
function _getType(obj) {
var type = toString.call(obj) || '';
type = type.replace(/(^\s?\[object\s?)|(\]\s?$)/gi, '').toLowerCase();
return type;
};
/* ====================================================================== */
/**
* 只能是{}
*/
function _isPlainObject(obj) {
if (!obj || _getType(obj) !== "object") {
return false;
}
var constructorName = '';
try {
constructorName = obj.constructor.toString();
} catch (error) {}
// 'Object() {[native code]}'
if (!/function\s*Object\(\s*\)\s*\{\s*\[native\s*code\]\s*\}\s*$/gi.test(constructorName)) {
return false;
}
return true;
};
};
/**
* 可複製3種格式({}, [], Map)
*
* g8
*/
function deepCopy(data) {
// debugger;
var toString = Object.prototype.toString;
var clone;
var type = _getType(data);
// 若(data)是(不含子孫物件)
if (data == null || (type != 'array' && type != 'map' && !_isPlainObject(data))) {
clone = data;
return clone;
}
/* ---------------------------------- */
// data含有子孫物件
if (type == 'map') {
clone = new Map();
} else if (type == 'array') {
clone = [];
} else {
clone = {};
}
copyChild(clone, data);
//////////////////////////////////////////////////
/**
* 會進來的(data)一定是有子孫元素
*/
function copyChild(target, data) {
// debugger;
var clone;
var dataType = _getType(data);
var tar_getType = _getType(target);
/* ---------------------------------- */
if (tar_getType != dataType) {
throw new Error('要拷貝的數據型態不同');
} else if (dataType == 'map') {
// 若是(Map)
data.forEach(function(child, key) {
var childType = _getType(child);
if (childType != 'array' && childType != 'map' && !_isPlainObject(child)) {
// 若子物件是單純數據
target.set(key, child);
} else {
// 若(child)物件還攜帶有子孫
// (clone)必須與(child)同型態
clone = _getInitClone(childType);
/* ------------------------ */
// 遞回,把child拷貝到 clone
copyChild(clone, child);
target.set(key, clone);
}
});
} else if (dataType == 'array') {
// 若是(array)
data.forEach(function(child, key) {
var childType = _getType(child);
if (childType != 'array' && childType != 'map' && !_isPlainObject(child)) {
// 若子物件是單純數據
target[key] = child;
} else {
// 若子物件還攜帶有子孫
// (clone)必須與(child)同型態
clone = _getInitClone(childType);
/* ------------------------ */
// 遞回,把child拷貝到 clone
copyChild(clone, child);
target[key] = clone;
}
});
} else if (_isPlainObject(data)) {
// 若(data)是(PlainObject)
for (var key in data) {
if (data.hasOwnProperty(key)) {
var child = data[key];
var childType = _getType(child);
if (childType != 'array' && childType != 'map' && !_isPlainObject(child)) {
// 若子物件是單純數據
target[key] = child;
} else {
// 若子物件還攜帶有子孫
// (clone)必須與(child)同型態
clone = _getInitClone(childType);
/* ------------------------ */
// 遞回,把child拷貝到 clone
copyChild(clone, child);
target[key] = clone;
}
}
}
} else {
throw new Error('data have no child');
}
};
/* ---------------------------------- */
return clone;
////////////////////////////////////////////////////////////////////////////
function _getInitClone(type) {
var clone;
switch (type) {
case 'array':
clone = [];
break;
case 'map':
clone = new Map();
break;
default:
clone = {};
break;
}
return clone;
};
/* ====================================================================== */
function _getType(obj) {
var type = toString.call(obj) || '';
type = type.replace(/(^\s?\[object\s?)|(\]\s?$)/gi, '').toLowerCase();
return type;
};
/* ====================================================================== */
/**
* 只能是{}
*/
function _isPlainObject(obj) {
if (!obj || _getType(obj) !== "object") {
return false;
}
var constructorName = '';
try {
constructorName = obj.constructor.toString();
} catch (error) {}
// 'Object() {[native code]}'
if (!/function\s*Object\(\s*\)\s*\{\s*\[native\s*code\]\s*\}\s*$/gi.test(constructorName)) {
return false;
}
return true;
};
};
2016年12月19日 星期一
(js)Promise.series用法
function job1() {
return new Promise(function (res, rej) {
console.log('p_1 start');
setTimeout(function () {
console.log('p_1 end');
res();
}, 2000);
});
}
function job2() {
return new Promise(function (res, rej) {
console.log('p_2 start');
setTimeout(function () {
console.log('p_2 end');
res();
}, 2000);
});
}
function job3() {
return new Promise(function (res, rej) {
console.log('p_3 start');
setTimeout(function () {
console.log('p_3 end');
res();
}, 2000);
});
}
/* ================================================ */
job1().then(function () {
return job2();
}).then(function () {
return job3();
});
(js)generator範例
function getFirstName() {
setTimeout(function() {
gen.next('a');
}, 1000);
}
function getSecondName(data) {
setTimeout(function() {
data.age = 15;
gen.next(data + ' b');
}, 1000);
}
function* sayHello() {
var a = yield getFirstName();
console.dir(a);
var b = yield getSecondName(a);
console.dir(b);
}
var gen = sayHello();
gen.next();
setTimeout(function() {
gen.next('a');
}, 1000);
}
function getSecondName(data) {
setTimeout(function() {
data.age = 15;
gen.next(data + ' b');
}, 1000);
}
function* sayHello() {
var a = yield getFirstName();
console.dir(a);
var b = yield getSecondName(a);
console.dir(b);
}
var gen = sayHello();
gen.next();
2016年12月16日 星期五
(php)在 NetBeans IDE 中調試 PHP 源代碼
在 NetBeans IDE 中調試 PHP 源代碼
目錄

- 準備工作
- 如何在 NetBeans IDE 中使用 XDebug 進行 PHP 調試
- 調試選項
- 使用工具欄和編輯器
- 調試器窗口
- 調試會話
- 樣例調試會話
- 使用其他監視
- PHP 和 HTML 混合用例
- 路徑映射、調試器代理以及在定製 URL 上啟動調試會話
要學習本教程,您需要具備以下軟件和資源。
軟件或資源 | 要求的版本 |
---|---|
NetBeans IDE | PHP 下載包 |
PHP 引擎 | 版本 5 |
Web 服務器 | 推薦使用 Apache HTTP Server 2.2。 |
PHP 調試器 | XDebug 2.0 或更高版本 |
準備工作
要在適用於 PHP 的 NetBeans IDE 中成功調試 PHP 應用程序,您需要為進行 PHP 開發安裝並配置 PHP 引擎、Apache 本地 Web 服務器和 XDebug 調試器。如果您難以使 XDebug 正常工作,請參見 NetBeans XDebug Wiki,並/或通過 users@php.netbeans.org 諮詢社區。
如何在 NetBeans IDE 中使用 XDebug 進行 PHP 調試
從 NetBeans IDE 中運行 XDebug 時,將在設置斷點的每行暫停執行 PHP 程序。當程序暫停執行時,XDebug 可以檢索有關當前程序狀態的信息,如程序變量的值。實際上,此過程可以用下列工作流來表示:
- 在應暫停執行 PHP 源代碼的每行設置斷點。
- 啟動調試會話。
- 當到達包含斷點的行時,可以按 F7 和 F8 鍵,以逐行執行腳本。在調試器窗口中監視應用程序的狀態。
- 關閉調試會話。
有關在 NetBeans IDE 中使用 XDebug 的工作流詳細信息,請參見調試會話。
NetBeans IDE 提供了一個調試工具欄,可幫助您逐步執行文件。請參見使用工具欄和編輯器。
調試選項
NetBeans IDE 的 "Options"(選項)中包含一個標籤,用於更改 PHP 調試的某些默認設置。要打開這些選項,請轉至 "Tools"(工具)> "Options"(選項)(在 Mac 上則轉至 "NetBeans" > "Preferences"(首選項)),然後依次選擇 "PHP" 選項和 "Debugging"(調試)標籤。
註:在 NetBeans IDE 版本 7.1 中引入了 "Debugging"(調試)標籤。早期版本的 NetBeans 在 "General PHP"(常規 PHP)標籤中具有調試選項。版本 7.1 中的部分選項在早期版本中不可用。

您可以在此面板中更改以下選項:
- Debugger port(調試器端口)。這是 XDebug 使用的端口,如 php.ini 中所設置。默認情況下為端口 9000。此對話框中的端口號必須與您在 php.ini 中設置的調試器端口相同。在此對話框中,不能改動 XDebug 使用的端口。只能將 XDebug 使用的端口通知 NetBeans IDE。
- Session ID(會話 ID)。調試會話的任意名稱。默認情況下為 netbeans-xdebug。如果需要設置 php.ini 中的 xdebug.idekey 屬性(如在某些遠程調試情況下),則必須注意該值。
- Stop at First Line(在第一行停止)。勾選此選項時,調試器會話在代碼的第一行停止,而不在第一個斷點處停止。在啟動調試會話時,該選項可使您的屏幕一直居於 IDE 中,而不切換至瀏覽器窗口。
- Watches and Balloon Evaluation(監視和氣球式求值)。默認情況下,將禁用監視和氣球式求值。監視和氣球式求值會導致 XDebug 不穩定。
- Maximum Depth of Structures(最大結構深度)。設置嵌套結構(如嵌套數組、對象中的對象等)的可視性
- Maximum Number of Children(最大子項數)。設置監視求值期間數組項的可視性。(如果將 "Maximum Number of Children"(最大子項數)設置為 1,則只會看到數組中的第一項,即使該數組有多個項也是如此。)
- Show Requested URLs(顯示請求的 URL)。調試期間打開新的 "Output"(輸出)窗口。此 "Output"(輸出)窗口名為 "PHP Requested Urls"(PHP 請求的 URL),它會顯示當前處理的 URL。這些 URL 是可單擊的。在 "Output"(輸出)窗口中單擊 URL,以便在瀏覽器窗口中打開該 URL。
- PHP Debugger Console(PHP 調試器控制台)。打開顯示已調試腳本輸出的新 "Output"(輸出)窗口。註:請設置 php.ini 文件中的 output_buffering = Off。否則,在 "Output"(輸出)窗口中,將會延遲顯示腳本輸出。
就本教程而言,您不需要更改任何這些設置,除非選擇性地啟用監視。
使用工具欄和編輯器
可以使用編輯器來查看文件內容。因此,在進行調試時,編輯器以及調試器工具欄可為您提供在執行期間逐步執行代碼的功能,以便查看文件內容如何影響在瀏覽器中執行的操作。
使用調試器工具欄
在運行調試會話時,將在編輯器上方顯示調試器工具欄。

工具欄提供了以下操作:
完成會話 (![]() | 完成調試會話 |
暫停 (![]() | 掛起調試會話 |
恢復 (![]() | 繼續調試會話 |
步過 (![]() | 越過執行語句 |
步入 (![]() | 步入函數調用 |
步出 (![]() | 步出當前函數調用 |
運行至光標位置 (![]() | 運行至光標位置 |
設置斷點
在文件中設置斷點,以便在執行期間通知調試器停止的位置。
重要提示:您必須在 PHP 代碼中設置斷點才能使用 XDebug。
要設置斷點,請在編輯器中單擊要設置斷點的行的左旁註處。

可以通過單擊斷點標記 (
) 刪除斷點。

此外,還可以暫時禁用斷點。要執行此操作,請右鍵單擊斷點標記,然後取消選中 "Breakpoint"(斷點)> "✔Enabled"(✔啟用)。這會將斷點切換為禁用狀態,從而導致一個灰色標記 (
) 顯示在左旁註中。

如果調試器在執行時遇到斷點,它將在斷點處停止,以便您在調試窗口中檢查變量,然後逐步執行斷點後面的任何代碼。

檢查工具提示
在調試會話期間掛起調試器時,可以在編輯器中將鼠標懸停在 PHP 標識符上以顯示工具提示。如果該標識符在選定調用堆棧框架中有效,則會顯示其值。此外,還可以選擇 PHP 表達式。該表達式的值將顯示在工具提示中。

調試器窗口
啟動調試會話時,將在主編輯器窗口下打開一組調試器窗口。在調試器窗口中,可以在逐步執行代碼時跟蹤變量和表達式值,檢查執行線程的調用堆棧,驗證源 URL 以及在會話之間切換(如果正在運行並發調試會話)。
- "Sessions"(會話)窗口
- "Variables"(變量)窗口
- "Watches"(監視)窗口
- "Call Stack"(調用堆棧)窗口
- "Threads"(線程)窗口
- "Sources"(源)窗口
- "Breakpoints"(斷點)窗口
可以從 IDE 的 "Window"(窗口)> "Debugging"(調試)菜單中訪問所有調試器窗口。在調試會話處於活動狀態後,便可以開始使用調試器窗口。

"Sessions"(會話)窗口
"Sessions"(會話)窗口顯示當前處於活動狀態的所有調試會話。在啟動 PHP 調試會話時,可以在 "Sessions"(會話)窗口中看到 PHP 調試器條目。

NetBeans IDE 還允許同時運行多個調試器會話。例如,可以同時調試 Java 和 PHP 項目。在這種情況下,可以標識在 "Sessions"(會話)窗口中列出的兩個會話。

當前會話(即您可使用調試器工具欄控制的會話)由更為醒目的圖標 (
) 指示。要切換會話,您可以雙擊要激活的會話,或者右鍵單擊非當前會話,然後選擇「激活」。

註:如果掛起了當前所在的會話,建議您不要切換會話。
您還可以使用右鍵單擊彈出式窗口終止會話(單擊鼠標右鍵,然後選擇 "Finish"(完成)),或者在調試會話中的當前線程或調試所有線程之間切換(單擊鼠標右鍵,然後選擇 "Scope"(範圍)> "Debug All Threads"(調試所有線程)或 "Debug Current Thread"(調試當前線程))。
"Variables"(變量)窗口
在掛起調試器後,"Variables"(變量)窗口將顯示選定調用堆棧框架的當前
window
對象的變量。在當前窗口中,將顯示每個變量的節點。超全局變量按單獨的節點進行分組。
在逐步執行代碼時,某些局部變量的值可能會發生變化。此類局部變量以粗體顯示在 "Local Variables"(局部變量)窗口中。您也可以直接單擊 "Values"(值)列並手動更改變量值。
"Watches"(監視)窗口
設置監視會導致 XDebug 不穩定,因此不建議這樣做。默認情況下,將禁用監視。不過,如果要設置監視,請參見使用其他監視。
"Call Stack"(調用堆棧)窗口
"Call Stack"(調用堆棧)窗口列出了在執行期間進行的調用序列。在掛起調試器時,"Call Stack"(調用堆棧)窗口將顯示函數調用序列(即調用堆棧)。在初次暫停時,將會自動選擇最頂部的堆棧框架。在該窗口中雙擊函數調用,即可在編輯器中轉至該行。如果對 PHP 類進行調用,則在雙擊該調用時,"Navigator"(導航器)窗口也將轉至該行。

"Threads"(線程)窗口
"Threads"(線程)窗口中會指出哪個 PHP 腳本當前處於活動狀態,以及是在斷點處掛起還是處於運行狀態。如果該腳本處於運行狀態,則需要轉至瀏覽器窗口,並與該腳本進行交互。

"Sources"(源)窗口
"Sources"(源)窗口顯示為調試會話加載的所有文件和腳本。對於 PHP 項目,"Sources"(源)窗口當前不起作用。
"Breakpoints"(斷點)窗口
可以使用 "Breakpoints"(斷點)窗口來查看在 IDE 中設置的所有斷點。

通過 "Breakpoints"(斷點)窗口,可以在 "Context"(上下文)窗口中啟用或禁用斷點。此外,還可以創建斷點組。
調試會話
以下過程是典型調試會話的工作流。
運行調試會話:- 啟動 IDE,然後打開包含要調試的源代碼的文件。
- 在要暫停調試器的每行設置斷點。要設置斷點,請將光標放在行首,然後按 Ctrl-F8/⌘-F8 組合鍵,或者選擇 "Debug"(調試)> "Toggle Line Breakpoint"(開啟/關閉行斷點)。
- 在 "Projects"(項目)窗口中,導航至當前項目節點,單擊鼠標右鍵,然後從彈出式菜單中選擇 "Debug"(調試)。IDE 將打開調試器窗口並在調試器中運行該項目,直至到達斷點為止。
註:如果當前項目設置為「主項目」,您可以選擇「調試」>「調試主項目」,按 Ctrl-F5,或單擊。
- 切換至 "Local Variables"(局部變量)窗口。該窗口顯示當前函數中已初始化的所有變量及其類型和值。
- 要查看該函數外部的變量值,請將光標置於此變量出現的某個位置上。工具提示會顯示變量值。
- 要逐行(包括所有被調用函數中的行)執行程序,請按 F7 鍵或選擇 "Debug"(調試)> "Step Into"(步入),然後在 "Local Variables"(局部變量)窗口中監視這些變量值的更改。
- 要通過監視表達式的更改來檢查程序邏輯,請定義一個新監視:
- 要打開 "Watches"(監視)窗口,請選擇 "Window"(窗口)> "Debugging"(調試)> "Watches"(監視),或者按 Ctrl-Shift-2 組合鍵。"Watches"(監視)窗口打開。
- 在 "Watches"(監視)窗口中的任意位置單擊鼠標右鍵,然後從彈出式菜單中選擇 "New Watch"(新建監視)。"New Watch"(新建監視)窗口打開。
- 輸入監視表達式,然後單擊 "OK"(確定)。
現在,您便可以在調試過程中進行其他檢查。重要提示:您必須在 PHP "Options"(選項)的 "Debugging"(調試)標籤中啟用監視才能設置監視。 - 要取消對某個函數中代碼的逐行執行操作並跳至該函數調用後的下一行,請按 Ctrl-F7/⌘-F7 組合鍵或選擇 "Debug"(調試)> "Step Out"(步出)。
- 要跳過對某個函數中代碼的逐行執行操作,獲取該函數返回的值,並跳至該函數調用後的下一行,請按 F8 鍵或選擇 "Debug"(調試)> "Step Over"(步過)。
- 要暫停調試會話,請選擇 "Debug"(調試)> "Pause"(暫停)。
- 要繼續調試會話,請選擇 "Debug"(調試)> "Continue"(繼續)或按
。
- 要取消調試會話,請按
。
- 在程序結束時,調試器窗口會關閉。
樣例調試會話
本部分中的樣例說明了基本的調試器函數,包括步入和步過函數。此外,還顯示了典型的調試器窗口輸出。- 使用以下參數創建新的 PHP 項目:
- 項目類型 - PHP 應用程序
- 源位置 - htdocs 文件夾的默認位置
- 運行配置 - "Local Web Site"(本地 Web 站點)
- 要在會話過程中使用熱鍵,請將光標置於項目節點上,然後從彈出式菜單中選擇 "Set as Main Project"(設置為主項目)。
- 在 index.php 文件中,輸入以下代碼:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>NetBeans PHP debugging sample</title> </head> <body> <?php $m=5; $n=10; $sum_of_factorials = calculate_sum_of_factorials ($m, $n); echo "The sum of factorials of the entered integers is " . $sum_of_factorials; function calculate_sum_of_factorials ($argument1, $argument2) { $factorial1 = calculate_factorial ($argument1); $factorial2 = calculate_factorial ($argument2); $result = calculate_sum ($factorial1, $factorial2); return $result; } function calculate_factorial ($argument) { $factorial_result = 1; for ($i=1; $i<=$argument; $i++) { $factorial_result = $factorial_result*$i; } return $factorial_result; } function calculate_sum ($argument1, $argument2) { return $argument1 + $argument2; } ?> </body> </html>
該代碼包含三個函數:- calculate_factorial () 函數
- calcualte_sum () 函數
- calculate_sum_of_factorials () 函數,該函數調用 calculate_factorial () 函數兩次,再調用 calcualte_sum () 函數一次,然後返回計算的階乘和。
- 在 PHP 塊的開頭設置一個斷點(Ctrl-F8/⌘-F8 組合鍵):
<?php
- 要開始調試,請單擊
。調試器將在斷點處停止。
- 按 F7 鍵三次。調試器將在調用函數 calculate_sum_of_factorials () 的行上停止。"Local Variables"(局部變量)窗口會顯示變量 $m 和 $n 以及它們的值:
- 要步入函數 calculate_sum_of_factorials (),請按 F7 鍵。調試器開始執行函數 calculate_sum_of_factorials () 中的代碼,然後在函數 calculate_factorial () 的調用處停止。
現在,"Local Variables"(局部變量)窗口將顯示函數 calculate_sum_of_factorials () 中聲明的局部變量 $argument1 和 $argument2。 - 按 F7 鍵。調試器開始執行函數 calculate_factorial () 中的代碼。"Call Stack"(調用堆棧)窗口將按倒序顯示函數的調用堆棧,最後調用的函數位於列表頂部:
- 按 F7 鍵步入循環。在 "Variables"(變量)窗口中查看變量值。
- 如果您確定代碼運行正常,請按 Ctrl-F7/⌘-F7 組合鍵,以取消函數執行。程序將在調用函數 calculate_factorial () 後返回至下一行。
註:您也可以按 F7 鍵,直到程序執行完函數 calculate_factorial () 為止。您也會在調用該函數後返回至下一行。 - 由於您剛檢查了函數 calculate_factorial (),並且確定其運行正常,因此可以跳過對該函數的再次執行操作(「步過」)。要越過該函數,請按 F8 鍵。程序將在函數 calculate_sum () 的調用處停止。
- 要步入函數 calculate_sum (),請按 F7 鍵。
- 要越過該函數,請按 F8 鍵。對於任何一種情況,調試器都會在函數 calculate_sum_of_factorials () 的最後一行停止。
- 按 F7 鍵。調試器將移到 echo 語句所在的行上。
- 按 F7 鍵,直到調試器退出程序為止。將打開瀏覽器窗口並顯示程序執行的結果:
使用其他監視
可以定義其他監視表達式來跟蹤程序的執行。這有助於捕獲錯誤。
警告:設置其他監視會導致 XDebug 不穩定。默認情況下,將禁用監視,請參見調試選項。
- 按如下所示更新代碼(將加號替換為減號):
function calculate_sum ($argument1, $argument2) { return $argument1 - argument2; }
假定運算符的改變是由於拼寫錯誤造成的,而實際上您需要計算和。 - 選擇 "Debug"(調試)> "New Watch"(新建監視),或者按 Ctrl/⌘-shift-F7 組合鍵。"New Watch"(新建監視)窗口打開。
- 輸入以下表達式,然後單擊 "OK"(確定)。
$factorial1+$factorial2
"Watches"(監視)窗口中將顯示新表達式。 - 運行調試會話。當調試器在以下行停止時
return $result;
將 "Watches"(監視)窗口中表達式的值與 "Local Variables"(局部變量)窗口中 $result 的值進行比較。它們應該相同,但在此示例中不同。
PHP 和 HTML 混合用例
您可以調試同時包含 PHP 塊和 HTML 塊的代碼。在樣例調試會話部分的示例中,對值進行了固定編碼。現在,將通過用於輸入值的 HTML 輸入窗體來擴展該代碼。- 將以下 HTML 代碼添加到 <?php ?> 塊下鍵入或粘貼以下代碼:
<form action="index.php" method="POST"> Enter the first integer, please: <input type="text" name="first_integer"/><br/> Enter the second integer, please: <input type="text" name="second_integer"/><br/> <input type="submit" name="enter" value="Enter"/> </form>
有關詳細信息,請參見 HTML 輸入窗體。 - 替換 <?php ?> 塊下鍵入或粘貼以下代碼:
$m=5; $n=10; $sum_of_factorials = calculate_sum_of_factorials ($m, $n); echo "The sum of factorials of the entered integers is " . $sum_of_factorials;
替換為以下代碼:if (array_key_exists ("first_integer", $_POST) && array_key_exists ("second_integer", $_POST)) { $result = calculate_sum_of_factorials ($_POST["first_integer"], $_POST["second_integer"]); echo "Sum of factorials is " . $result; }
- 在 <?php ?> 塊的開頭設置斷點,然後啟動調試會話。
- 按 F7 鍵。調試器將步入程序。同時,會打開瀏覽器窗口,但不顯示輸入窗體。這是調試器的正確行為,因為它必須首先通過 Web 頁的整個源代碼,然後才能顯示該頁面。實際上,這意味著調試器通過了兩次代碼。第一次是調試器處理代碼以顯示 HTML 輸入窗體。第二次是調試器逐步執行 PHP 代碼。
- 按 F7 鍵,直到調試器到達程序末尾並且打開輸入窗體為止。
- 填寫該窗體,然後單擊 Enter 鍵。將繼續調試會話(如樣例調試會話部分中所述)。
路徑映射、調試器代理以及在定製 URL 上啟動調試會話
可以調試腳本和 Web 頁,還可以在本地或遠程調試 Web 頁。遺憾的是,對於遠程調試,在遠程服務器上調試的 PHP 文件與在本地計算機上運行的 NetBeans IDE 中打開的文件並不相同。因此,NetBeans 中的調試器支持必須能夠將服務器路徑映射到本地路徑。然而,由於存在諸多複雜因素,無法針對各種情況自動解決路徑映射問題。因此,從 NetBeans 6.7 開始,您可以通過項目設置針對各個運行配置手動定義路徑映射。此外,還可以指定代理服務器(如果有),以及在其上啟動調試會話的 URL。如果未指定此 URL,則將從索引文件開始執行調試。
設置路徑映射並啟用定製調試 URL:
- 在 "Projects"(項目)窗口中右鍵單擊項目節點,然後從上下文菜單中打開項目的 "Properties"(屬性)。
- 在 "Project Properties"(項目屬性)對話框中,轉至 "Run Configuration"(運行配置)類別。
- 單擊 "Advanced"(高級)按鈕。"Advanced Web Configuration"(高級 Web 配置)對話框打開。
- 添加要進行路徑映射的服務器路徑和項目路徑。
- 在 "Debug URL"(調試 URL)下,選中以下一個選項(不要將默認值保留為選中狀態):
- "Ask Every Time"(每次都詢問):讓 IDE 提示您在啟動調試會話時輸入 URL。
- "Do Not Open Web Browser"(不打開 Web 瀏覽器):需要您手動打開瀏覽器並輸入 URL(您需要 GET/POST XDEBUG_SESSION_START 變量)。
- 使用代理服務器進行調試時,請在 "Debugger Proxy"(調試器代理)類別中輸入該服務器的主機名和端口。
有關詳細信息,請參見 "NetBeans for PHP"(NetBeans PHP) 博客中的 Path Mapping in PHP Debugger(使用 PHP 調試器進行路徑映射)的帖子。
要發送意見和建議、獲得支持以及隨時瞭解 NetBeans IDE PHP 開發功能的最新開發情況,請加入 users@php.netbeans.org 郵件列表。
(js)Number()與ParseInt()的差異
<< true >>
parseInt(): NaN
Number(): 1
-----------------------
<< null >>
parseInt(): NaN
Number(): 0
-----------------------
<< undefined >>
parseInt(): NaN
Number(): NaN
-----------------------
<< 0 >>
parseInt(): 0
Number(): 0
-----------------------
<< function isNaN() { [native code] } >>
parseInt(): NaN
Number(): NaN
-----------------------
<< {} >>
parseInt(): NaN
Number(): NaN
-----------------------
<< [] >>
parseInt(): NaN
Number(): 0
-----------------------
<< "" >>
parseInt(): NaN
Number(): 0
-----------------------
<< 123.56 >>
parseInt(): 123
Number(): 123.56
-----------------------
<< "123.56#" >>
parseInt(): 123
Number(): NaN
-----------------------
<< function () {
} >>
parseInt(): NaN
Number(): NaN
-----------------------
parseInt(): NaN
Number(): 1
-----------------------
<< null >>
parseInt(): NaN
Number(): 0
-----------------------
<< undefined >>
parseInt(): NaN
Number(): NaN
-----------------------
<< 0 >>
parseInt(): 0
Number(): 0
-----------------------
<< function isNaN() { [native code] } >>
parseInt(): NaN
Number(): NaN
-----------------------
<< {} >>
parseInt(): NaN
Number(): NaN
-----------------------
<< [] >>
parseInt(): NaN
Number(): 0
-----------------------
<< "" >>
parseInt(): NaN
Number(): 0
-----------------------
<< 123.56 >>
parseInt(): 123
Number(): 123.56
-----------------------
<< "123.56#" >>
parseInt(): 123
Number(): NaN
-----------------------
<< function () {
} >>
parseInt(): NaN
Number(): NaN
-----------------------
(js)series(鍊結方法版)
// 註冊錯誤處理函式
$Series_.reject('b', error_fun);
$Series_('b', b_1);
$Series_('b', b_2); // 會拋出(reject)
$Series_('b', b_3);
///////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
*
*/
function $Series_(jobName, fn) {
return $Series_._addSeries(jobName, fn);
};
/* ========================================================================== */
/**
* ($Series_)的參數
*/
(function(self) {
self.jobList = {};
// 若沒有指定任務名,就用此預設的
self.defaultJobName = 'fx';
// 每個(job)本身要帶的資料
self.defaultJobData = {
'last': undefined, // 每個任務列最後一個工作節點
'carryValue': undefined, // 紀錄每個工作節點的返回值
'active': false, // 任務列是否在執行中
'count': 0, // 等待中的數量
'reject': undefined // 有(reject)過會放這
};
})($Series_);
/* ========================================================================== */
/**
* ($Series_)方法集
*/
(function(self) {
/**
* 把任務加入隊列
*
* 若隊列閒置,就執行
*/
self._addSeries = function(jobName, fn) {
debugger;
if (typeof jobName == 'function') {
jobName = self.defaultJobName;
fn = jobName;
}
jobName = jobName || self.defaultJobData;
/* ---------------------------------- */
/**
* 調出(job)
*/
var job;
if (typeof self.jobList[jobName] == 'undefined') {
self.jobList[jobName] = Object.create(self.defaultJobData);
}
job = self.jobList[jobName];
/* -------------------------------------------- */
var Series = self.prototype.series;
var now = new Series(jobName);
/* -------------------------------------------- */
/**
* 處理節點間的關係
*/
// 掛勾
(job.last) && (job.last.next = now)
job.last = now;
++job.count; // 通知(job)多加一個項目
/* -------------------------------------------- */
// 設定隊列要執行的事項
now.setCallBack(fn);
/* -------------------------------------------- */
/**
* 若沒在執行中
*
* 強制啟動
*/
(!job.active) && (now.action());
/* -------------------------------------------- */
return now;
};
/* ====================================================================== */
/**
* 註冊錯誤事件(API)
*/
self.reject = function(jobName, fn) {
debugger;
// 處理進來的參數
if (arguments.length == 1) {
if (typeof jobName == 'function') {
fn = jobName;
jobName = self.defaultJobName;
} else if (jobName == null) {
jobName = self.defaultJobName;
fn = undefined;
}
}
jobName = jobName || self.defaultJobName;
/* ---------------------------------- */
var jobData;
if (typeof self.jobList[jobName] == 'undefined') {
self.jobList[jobName] = Object.create(self.defaultJobData);
}
jobData = self.jobList[jobName];
/* ---------------------------------- */
jobData.reject = (typeof fn == 'function') ? fn : undefined;
};
})($Series_);
/* ========================================================================== */
(function(_self) {
// debugger;
/**
* 核心(序列)
*/
function Series(jobName) {
var self = this;
// 下一個要執行的(series)
this.next;
this.fn = Series;
/* ---------------------------------- */
// 要執行的任務
this.callBack;
/* ---------------------------------- */
/**
* 參考所屬的工作隊列($Async_.jobList)
*
* 主要在設定(job.carryValue, active, last)
*/
this.job;
this.jobName = jobName || '';
/* ================================================================== */
this.__construct = function() {
// debugger;
// 取得所屬(job)
this.job = _self.jobList[this.jobName];
};
/* ================================================================== */
/**
* call by window
*
* 要丟給(this.callBack)的參數
* 任務結束時,要執行此,通知任務結束,開始下一步
*
*/
this._resolve = function(value) {
// debugger;
--self.job.count;
if (arguments.length) {
self.job.carryValue = value;
}
if (self.next) {
// 若有下一步,執行下一步的(callBack)
self.next.action();
} else {
// 沒有下一步,通知隊列結束
self.job.last = undefined;
self.job.active = false;
}
};
/* ================================================================== */
/**
* call by window
*
* 隊列出錯,呼叫此
* reset隊列,方便以後呼叫
*
*/
this._reject = function(data) {
// debugger;
/* ---------------------------------- */
// 要傳送的資訊
var carryValue = undefined;
if (typeof self.job.carryValue == 'object') {
carryValue = Object.create(self.job.carryValue);
} else if (typeof self.job.carryValue != 'undefined') {
carryValue = self.job.carryValue;
}
var _data = {
'count': self.job.count,
'data': carryValue,
'error': data
};
/* ---------------------------------- */
self.job.active = false;
self.job.carryValue = undefined;
self.job.last = undefined;
self.job.count = 0;
var reject_fun = self.job.reject;
self.job.reject = undefined;
/* ---------------------------------- */
(typeof reject_fun == 'function') && reject_fun(_data);
};
/* ================================================================== */
this.__construct();
};
////////////////////////////////////////////////////////////////////////////
(function() {
/**
* 主要任務(callBack)
*
* 執行(this.callBack)
*/
this.action = function() {
// debugger;
var self = this;
// 通知隊列在執行中
this.job.active = true;
/* -------------------------------------------- */
if (typeof this.callBack != 'function') {
return;
}
var info = {
'active': this.job.active,
'count': this.job.count,
'data': this.job.carryValue,
'reject': this.job.reject
};
// 執行任務(resolve, reject, carryValue, otherInfo)
setTimeout(function() {
self.callBack(self._resolve, self._reject, self.job.carryValue, info);
}, 0);
};
/* ================================================================== */
}).call(Series.prototype);
/* ====================================================================== */
(function() {
this.__setGet = function() {};
/* ================================================================== */
/**
* 設定要執行的(this.callBack)
*/
this.setCallBack = function(fn) {
if (typeof fn == 'function') {
this.callBack = fn;
}
};
}).call(Series.prototype);
/* ====================================================================== */
_self.prototype.series = Series;
})($Series_);
$Series_.reject('b', error_fun);
$Series_('b', b_1);
$Series_('b', b_2); // 會拋出(reject)
$Series_('b', b_3);
///////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
*
*/
function $Series_(jobName, fn) {
return $Series_._addSeries(jobName, fn);
};
/* ========================================================================== */
/**
* ($Series_)的參數
*/
(function(self) {
self.jobList = {};
// 若沒有指定任務名,就用此預設的
self.defaultJobName = 'fx';
// 每個(job)本身要帶的資料
self.defaultJobData = {
'last': undefined, // 每個任務列最後一個工作節點
'carryValue': undefined, // 紀錄每個工作節點的返回值
'active': false, // 任務列是否在執行中
'count': 0, // 等待中的數量
'reject': undefined // 有(reject)過會放這
};
})($Series_);
/* ========================================================================== */
/**
* ($Series_)方法集
*/
(function(self) {
/**
* 把任務加入隊列
*
* 若隊列閒置,就執行
*/
self._addSeries = function(jobName, fn) {
debugger;
if (typeof jobName == 'function') {
jobName = self.defaultJobName;
fn = jobName;
}
jobName = jobName || self.defaultJobData;
/* ---------------------------------- */
/**
* 調出(job)
*/
var job;
if (typeof self.jobList[jobName] == 'undefined') {
self.jobList[jobName] = Object.create(self.defaultJobData);
}
job = self.jobList[jobName];
/* -------------------------------------------- */
var Series = self.prototype.series;
var now = new Series(jobName);
/* -------------------------------------------- */
/**
* 處理節點間的關係
*/
// 掛勾
(job.last) && (job.last.next = now)
job.last = now;
++job.count; // 通知(job)多加一個項目
/* -------------------------------------------- */
// 設定隊列要執行的事項
now.setCallBack(fn);
/* -------------------------------------------- */
/**
* 若沒在執行中
*
* 強制啟動
*/
(!job.active) && (now.action());
/* -------------------------------------------- */
return now;
};
/* ====================================================================== */
/**
* 註冊錯誤事件(API)
*/
self.reject = function(jobName, fn) {
debugger;
// 處理進來的參數
if (arguments.length == 1) {
if (typeof jobName == 'function') {
fn = jobName;
jobName = self.defaultJobName;
} else if (jobName == null) {
jobName = self.defaultJobName;
fn = undefined;
}
}
jobName = jobName || self.defaultJobName;
/* ---------------------------------- */
var jobData;
if (typeof self.jobList[jobName] == 'undefined') {
self.jobList[jobName] = Object.create(self.defaultJobData);
}
jobData = self.jobList[jobName];
/* ---------------------------------- */
jobData.reject = (typeof fn == 'function') ? fn : undefined;
};
})($Series_);
/* ========================================================================== */
(function(_self) {
// debugger;
/**
* 核心(序列)
*/
function Series(jobName) {
var self = this;
// 下一個要執行的(series)
this.next;
this.fn = Series;
/* ---------------------------------- */
// 要執行的任務
this.callBack;
/* ---------------------------------- */
/**
* 參考所屬的工作隊列($Async_.jobList)
*
* 主要在設定(job.carryValue, active, last)
*/
this.job;
this.jobName = jobName || '';
/* ================================================================== */
this.__construct = function() {
// debugger;
// 取得所屬(job)
this.job = _self.jobList[this.jobName];
};
/* ================================================================== */
/**
* call by window
*
* 要丟給(this.callBack)的參數
* 任務結束時,要執行此,通知任務結束,開始下一步
*
*/
this._resolve = function(value) {
// debugger;
--self.job.count;
if (arguments.length) {
self.job.carryValue = value;
}
if (self.next) {
// 若有下一步,執行下一步的(callBack)
self.next.action();
} else {
// 沒有下一步,通知隊列結束
self.job.last = undefined;
self.job.active = false;
}
};
/* ================================================================== */
/**
* call by window
*
* 隊列出錯,呼叫此
* reset隊列,方便以後呼叫
*
*/
this._reject = function(data) {
// debugger;
/* ---------------------------------- */
// 要傳送的資訊
var carryValue = undefined;
if (typeof self.job.carryValue == 'object') {
carryValue = Object.create(self.job.carryValue);
} else if (typeof self.job.carryValue != 'undefined') {
carryValue = self.job.carryValue;
}
var _data = {
'count': self.job.count,
'data': carryValue,
'error': data
};
/* ---------------------------------- */
self.job.active = false;
self.job.carryValue = undefined;
self.job.last = undefined;
self.job.count = 0;
var reject_fun = self.job.reject;
self.job.reject = undefined;
/* ---------------------------------- */
(typeof reject_fun == 'function') && reject_fun(_data);
};
/* ================================================================== */
this.__construct();
};
////////////////////////////////////////////////////////////////////////////
(function() {
/**
* 主要任務(callBack)
*
* 執行(this.callBack)
*/
this.action = function() {
// debugger;
var self = this;
// 通知隊列在執行中
this.job.active = true;
/* -------------------------------------------- */
if (typeof this.callBack != 'function') {
return;
}
var info = {
'active': this.job.active,
'count': this.job.count,
'data': this.job.carryValue,
'reject': this.job.reject
};
// 執行任務(resolve, reject, carryValue, otherInfo)
setTimeout(function() {
self.callBack(self._resolve, self._reject, self.job.carryValue, info);
}, 0);
};
/* ================================================================== */
}).call(Series.prototype);
/* ====================================================================== */
(function() {
this.__setGet = function() {};
/* ================================================================== */
/**
* 設定要執行的(this.callBack)
*/
this.setCallBack = function(fn) {
if (typeof fn == 'function') {
this.callBack = fn;
}
};
}).call(Series.prototype);
/* ====================================================================== */
_self.prototype.series = Series;
})($Series_);
訂閱:
文章 (Atom)